session-android/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java

954 lines
38 KiB
Java
Raw Normal View History

/*
2011-12-20 19:20:44 +01:00
* Copyright (C) 2011 Whisper Systems
* Copyright (C) 2013-2017 Open Whisper Systems
2012-09-10 01:10:46 +02:00
*
2011-12-20 19:20:44 +01:00
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
2012-09-10 01:10:46 +02:00
*
2011-12-20 19:20:44 +01:00
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import static org.session.libsession.utilities.GroupUtil.CLOSED_GROUP_PREFIX;
import static org.session.libsession.utilities.GroupUtil.OPEN_GROUP_PREFIX;
import static org.thoughtcrime.securesms.database.GroupDatabase.GROUP_ID;
2011-12-20 19:20:44 +01:00
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
2011-12-20 19:20:44 +01:00
import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
Add emoji reacts support (#889) * feat: Add emoji reacts support * Remove message multi-selection * Add emoji reaction model * Add emoji reaction panel * Blur reacts panel background * Show emoji keyboard * Add emoji sprites * Update reaction proto * Emoji database updates * Emoji database refactor * Emoji reaction persistence * Optimize reactions retrieval * Fix emoji group query * Display emojis * Fix emoji persistence * Cleanup * Persistence refactor * Add reactions bottom sheet * Cleanup * Ui tweaks * React with any emoji * Show emoji react notifications * Remove reaction * Show reactions modal on long press * Click to react (+1) with an emoji * Click to react with an emoji * Enable emoji expand/collapse * fix: some compile issues from merge conflicts * fix: compile issues merging quote and media message UI * fix: xml IDs and adding in legacy is selected for future inclusion * Fix view constraints * Fix merge issue * Add message selection option in conversation context menu * Add sogs emoji integration * Handle sogs emoji reactions * Enable sending/deleting sogs emojis * fix: improve the visible message layout * fix: add file IDs to request parameters for message send (#940) * Fix open group polling from seqno instead of last hash (#939) * fix: reset seqno to get recent messages from open groups * build: upgrade build numbers * fix: actually run the migration * Using StringBuilder to construct request url * Fix reaction filter * fix: is_mms added in second projection query * Update default emojis * fix: include legacy and new open groups in server ID tracking (#941) * feat: add hidden moderator and admin roles, separated as they may be used independently in future (#942) * Cleanup * Fix view constraints * Add reactions capability check * Fix reactions alignment * Ui fixes * Display reactions list * feat: add formatted count strings * fix: account for negatives and add tests * Migrate old official open group locations for polling and adding (#932) * feat: adding in first part of open group migrations and tests for migration logic / helpers * feat: test code and migration logic for open groups in the case of no conflicts * feat: add in extra test cases and refactor code for migrator * refactor: migrate open group join URLs and references to server in adding new open groups to catch legacy and re-write it * refactor: joining open groups using OpenGroupUrlParser.kt now * fix: add in compile issues for renamed OpenGroupApi.kt from OpenGroupV2 * fix: prevent duplicates of http/https for new open group DNS and prevent adding new groups based on public key * fix: room and server swapped parameters * fix: replace default server for config messages * fix: actually using public key to de-dupe didn't work for rooms * build: bump version code and name * Display reactions list on open groups for moderators * Ui tweaks * Ui tweaks for moderation * Refactor * fix: compile issue * fix: de-duping joined queries in the get X from cursor * Restore import * fix: colouring the reaction overlay scrubber * fix: highlight colour, show reaction count if 1 or above * Cleanup * fix: light mode accent * fix: light / dark mode themeing in reactions dialog fragment * Emoji notification blinded id check * fix: show reaction list correctly and pass isUserModerator to bind methods * fix: remove unnecessary places for the moderator * fix: X button for removing own react not showing up properly * feat: add clear all header view * fix: migrate the clear all to the correct location * fix: use display instead of base * Truncate emoji sender ids * feat: add notify thread function in thread db * Notify threads on reaction received * fix: design fixes for the reaction list * fix: emoji reactions bottom sheet dialog UI designs * feat: add unsupported emoji reaction * fix: crash and doing vector properly * Fix reaction database queries * Fix background open group adder job * Show new open group reactions * Fetch a maximum of 5 reactors * Handle open group reactions polling conflicts * Add count to user reaction * Show number of additional reactors * fix: unreads set same as the unread query * fix: design changes * fix: update dependency to improve flexboxlayout behaviour, design consistencies * Add select message icon and update long press menu items order and wording * Fix crash on reactors dialog * fix: colours and backgrounds to match designs * fix: add header in recipient item * fix: margins * fix: alignments and layout issues for emoji reactions view * feat: add overflow previews and logic for overflow * Dim action bar * Add emoji search * Search index fix * Set count for 1:1 and closed group reactions when inserting in local database * Use on screen toolbar to allow overlaying * Show/hide scroll to bottom button * feat: add extended properties so it doesn't collapse on re-bind * Cleanup * feat: prevent keeping extended on rebinding if we get a new message ID * fix: long press works on devices now, fix release lint issue and crash for emoji search DBs from emoji builds * Display message timestamp * Fix modal items alignment * fix: sort order and emoji count in compareTo * Scale down really large messages to fit * Prevent closed group crash * Fix reaction author Co-authored-by: charles <charles@oxen.io> Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-09-04 13:03:32 +02:00
import org.jetbrains.annotations.NotNull;
2021-05-18 08:11:38 +02:00
import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.Contact;
import org.session.libsession.utilities.DelimiterUtil;
import org.session.libsession.utilities.DistributionTypes;
import org.session.libsession.utilities.GroupRecord;
import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.recipients.Recipient.RecipientSettings;
Add Session Id blinding (#862) * feat: Add Session Id blinding Including modified version of lazysodium-android to expose missing libsodium functions, we could build from a fork which we still need to setup. * Add v4 onion request handling * Update SOGS signature construction * Fix SOGS signature construction * Update onion request * Update signature data * Keep path prefixes for v4 endpoints * Update SOGS signature message * Rename to remove api version suffix * Update onion response parsing * Refactor file download paths * Implement request batching * Refactor batch response handling * Handle batch endpoint responses * Update batch endpoint responses * Update attachment download handling * Handle file downloads * Handle inbox messages * Fix issue with file downloads * Preserve image bytearray encoding * Refactor * Open group message requests * Check id blinding in user detail bottom sheet rather * Message validation refactor * Cache last inbox/outbox server ids * Update message encryption/decryption * Refactor * Refactor * Bypass user details bottom sheet in open groups for blinded session ids * Fix capabilities call auth * Refactor * Revert default server details * Update sodium dependency to forked repo * Fix attachment upload * Revert "Update sodium dependency to forked repo" This reverts commit c7db9529f900d09585ab94e440f6645faa88544e. * Add signed sodium lib * Update contact id truncation and mention logic * Open group inbox messaging fix * Refactor * Update blinded id check * Fix open group message sends * Fix crash on open group direct message send * Direct message refactor * Direct message encrypt/decrypt fixes * Use updated curve25519 version * Updated lazysodium dependency * Update encryption/decryption calls * Handle direct message parse errors * Minor refactor * Existing chat refactor * Update encryption & decryption parameters * Fix authenticated ciphertext size * Set direct message sync target * Update direct message thread lookup * Add blinded id mapping table * Add blinded id mapping table * Update threads after sends * Update open group message timestamp handling * Filter unblinded contacts * Format blinded id mentions * Add message deleted field * Hide open group inbox id * Update message request response handling * Update message request response sender handling * Fix mentions of blinded ids * Handle open group poll failure * fix: add log for failed open group onion request, add decoding body for blinding required error at destination * fix: change the error check * Persist group members * Reschedule polling after capabilities update * Retry on other exceptions * Minor refactor * Open group profile fix * Group member db schema update * Fix ban request key * Update ban response type * Ban endpoint updates * Ban endpoint updates * Delete messages Co-authored-by: charles <charles@oxen.io> Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-08-10 10:17:48 +02:00
import org.session.libsignal.utilities.IdPrefix;
import org.session.libsignal.utilities.Log;
2021-05-18 01:26:08 +02:00
import org.session.libsignal.utilities.Pair;
import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
Add emoji reacts support (#889) * feat: Add emoji reacts support * Remove message multi-selection * Add emoji reaction model * Add emoji reaction panel * Blur reacts panel background * Show emoji keyboard * Add emoji sprites * Update reaction proto * Emoji database updates * Emoji database refactor * Emoji reaction persistence * Optimize reactions retrieval * Fix emoji group query * Display emojis * Fix emoji persistence * Cleanup * Persistence refactor * Add reactions bottom sheet * Cleanup * Ui tweaks * React with any emoji * Show emoji react notifications * Remove reaction * Show reactions modal on long press * Click to react (+1) with an emoji * Click to react with an emoji * Enable emoji expand/collapse * fix: some compile issues from merge conflicts * fix: compile issues merging quote and media message UI * fix: xml IDs and adding in legacy is selected for future inclusion * Fix view constraints * Fix merge issue * Add message selection option in conversation context menu * Add sogs emoji integration * Handle sogs emoji reactions * Enable sending/deleting sogs emojis * fix: improve the visible message layout * fix: add file IDs to request parameters for message send (#940) * Fix open group polling from seqno instead of last hash (#939) * fix: reset seqno to get recent messages from open groups * build: upgrade build numbers * fix: actually run the migration * Using StringBuilder to construct request url * Fix reaction filter * fix: is_mms added in second projection query * Update default emojis * fix: include legacy and new open groups in server ID tracking (#941) * feat: add hidden moderator and admin roles, separated as they may be used independently in future (#942) * Cleanup * Fix view constraints * Add reactions capability check * Fix reactions alignment * Ui fixes * Display reactions list * feat: add formatted count strings * fix: account for negatives and add tests * Migrate old official open group locations for polling and adding (#932) * feat: adding in first part of open group migrations and tests for migration logic / helpers * feat: test code and migration logic for open groups in the case of no conflicts * feat: add in extra test cases and refactor code for migrator * refactor: migrate open group join URLs and references to server in adding new open groups to catch legacy and re-write it * refactor: joining open groups using OpenGroupUrlParser.kt now * fix: add in compile issues for renamed OpenGroupApi.kt from OpenGroupV2 * fix: prevent duplicates of http/https for new open group DNS and prevent adding new groups based on public key * fix: room and server swapped parameters * fix: replace default server for config messages * fix: actually using public key to de-dupe didn't work for rooms * build: bump version code and name * Display reactions list on open groups for moderators * Ui tweaks * Ui tweaks for moderation * Refactor * fix: compile issue * fix: de-duping joined queries in the get X from cursor * Restore import * fix: colouring the reaction overlay scrubber * fix: highlight colour, show reaction count if 1 or above * Cleanup * fix: light mode accent * fix: light / dark mode themeing in reactions dialog fragment * Emoji notification blinded id check * fix: show reaction list correctly and pass isUserModerator to bind methods * fix: remove unnecessary places for the moderator * fix: X button for removing own react not showing up properly * feat: add clear all header view * fix: migrate the clear all to the correct location * fix: use display instead of base * Truncate emoji sender ids * feat: add notify thread function in thread db * Notify threads on reaction received * fix: design fixes for the reaction list * fix: emoji reactions bottom sheet dialog UI designs * feat: add unsupported emoji reaction * fix: crash and doing vector properly * Fix reaction database queries * Fix background open group adder job * Show new open group reactions * Fetch a maximum of 5 reactors * Handle open group reactions polling conflicts * Add count to user reaction * Show number of additional reactors * fix: unreads set same as the unread query * fix: design changes * fix: update dependency to improve flexboxlayout behaviour, design consistencies * Add select message icon and update long press menu items order and wording * Fix crash on reactors dialog * fix: colours and backgrounds to match designs * fix: add header in recipient item * fix: margins * fix: alignments and layout issues for emoji reactions view * feat: add overflow previews and logic for overflow * Dim action bar * Add emoji search * Search index fix * Set count for 1:1 and closed group reactions when inserting in local database * Use on screen toolbar to allow overlaying * Show/hide scroll to bottom button * feat: add extended properties so it doesn't collapse on re-bind * Cleanup * feat: prevent keeping extended on rebinding if we get a new message ID * fix: long press works on devices now, fix release lint issue and crash for emoji search DBs from emoji builds * Display message timestamp * Fix modal items alignment * fix: sort order and emoji count in compareTo * Scale down really large messages to fit * Prevent closed group crash * Fix reaction author Co-authored-by: charles <charles@oxen.io> Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-09-04 13:03:32 +02:00
import org.thoughtcrime.securesms.groups.OpenGroupMigrator;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
2021-01-13 07:11:30 +01:00
import java.io.Closeable;
Add emoji reacts support (#889) * feat: Add emoji reacts support * Remove message multi-selection * Add emoji reaction model * Add emoji reaction panel * Blur reacts panel background * Show emoji keyboard * Add emoji sprites * Update reaction proto * Emoji database updates * Emoji database refactor * Emoji reaction persistence * Optimize reactions retrieval * Fix emoji group query * Display emojis * Fix emoji persistence * Cleanup * Persistence refactor * Add reactions bottom sheet * Cleanup * Ui tweaks * React with any emoji * Show emoji react notifications * Remove reaction * Show reactions modal on long press * Click to react (+1) with an emoji * Click to react with an emoji * Enable emoji expand/collapse * fix: some compile issues from merge conflicts * fix: compile issues merging quote and media message UI * fix: xml IDs and adding in legacy is selected for future inclusion * Fix view constraints * Fix merge issue * Add message selection option in conversation context menu * Add sogs emoji integration * Handle sogs emoji reactions * Enable sending/deleting sogs emojis * fix: improve the visible message layout * fix: add file IDs to request parameters for message send (#940) * Fix open group polling from seqno instead of last hash (#939) * fix: reset seqno to get recent messages from open groups * build: upgrade build numbers * fix: actually run the migration * Using StringBuilder to construct request url * Fix reaction filter * fix: is_mms added in second projection query * Update default emojis * fix: include legacy and new open groups in server ID tracking (#941) * feat: add hidden moderator and admin roles, separated as they may be used independently in future (#942) * Cleanup * Fix view constraints * Add reactions capability check * Fix reactions alignment * Ui fixes * Display reactions list * feat: add formatted count strings * fix: account for negatives and add tests * Migrate old official open group locations for polling and adding (#932) * feat: adding in first part of open group migrations and tests for migration logic / helpers * feat: test code and migration logic for open groups in the case of no conflicts * feat: add in extra test cases and refactor code for migrator * refactor: migrate open group join URLs and references to server in adding new open groups to catch legacy and re-write it * refactor: joining open groups using OpenGroupUrlParser.kt now * fix: add in compile issues for renamed OpenGroupApi.kt from OpenGroupV2 * fix: prevent duplicates of http/https for new open group DNS and prevent adding new groups based on public key * fix: room and server swapped parameters * fix: replace default server for config messages * fix: actually using public key to de-dupe didn't work for rooms * build: bump version code and name * Display reactions list on open groups for moderators * Ui tweaks * Ui tweaks for moderation * Refactor * fix: compile issue * fix: de-duping joined queries in the get X from cursor * Restore import * fix: colouring the reaction overlay scrubber * fix: highlight colour, show reaction count if 1 or above * Cleanup * fix: light mode accent * fix: light / dark mode themeing in reactions dialog fragment * Emoji notification blinded id check * fix: show reaction list correctly and pass isUserModerator to bind methods * fix: remove unnecessary places for the moderator * fix: X button for removing own react not showing up properly * feat: add clear all header view * fix: migrate the clear all to the correct location * fix: use display instead of base * Truncate emoji sender ids * feat: add notify thread function in thread db * Notify threads on reaction received * fix: design fixes for the reaction list * fix: emoji reactions bottom sheet dialog UI designs * feat: add unsupported emoji reaction * fix: crash and doing vector properly * Fix reaction database queries * Fix background open group adder job * Show new open group reactions * Fetch a maximum of 5 reactors * Handle open group reactions polling conflicts * Add count to user reaction * Show number of additional reactors * fix: unreads set same as the unread query * fix: design changes * fix: update dependency to improve flexboxlayout behaviour, design consistencies * Add select message icon and update long press menu items order and wording * Fix crash on reactors dialog * fix: colours and backgrounds to match designs * fix: add header in recipient item * fix: margins * fix: alignments and layout issues for emoji reactions view * feat: add overflow previews and logic for overflow * Dim action bar * Add emoji search * Search index fix * Set count for 1:1 and closed group reactions when inserting in local database * Use on screen toolbar to allow overlaying * Show/hide scroll to bottom button * feat: add extended properties so it doesn't collapse on re-bind * Cleanup * feat: prevent keeping extended on rebinding if we get a new message ID * fix: long press works on devices now, fix release lint issue and crash for emoji search DBs from emoji builds * Display message timestamp * Fix modal items alignment * fix: sort order and emoji count in compareTo * Scale down really large messages to fit * Prevent closed group crash * Fix reaction author Co-authored-by: charles <charles@oxen.io> Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-09-04 13:03:32 +02:00
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
2012-09-10 01:10:46 +02:00
import java.util.List;
import java.util.Map;
2012-09-10 01:10:46 +02:00
import java.util.Set;
2011-12-20 19:20:44 +01:00
public class ThreadDatabase extends Database {
private static final String TAG = ThreadDatabase.class.getSimpleName();
2021-05-21 03:09:03 +02:00
private final Map<Long, Address> addressCache = new HashMap<>();
public static final String TABLE_NAME = "thread";
2017-09-16 07:38:53 +02:00
public static final String ID = "_id";
public static final String DATE = "date";
public static final String MESSAGE_COUNT = "message_count";
public static final String ADDRESS = "recipient_ids";
public static final String SNIPPET = "snippet";
private static final String SNIPPET_CHARSET = "snippet_cs";
public static final String READ = "read";
public static final String UNREAD_COUNT = "unread_count";
2017-09-16 07:38:53 +02:00
public static final String TYPE = "type";
private static final String ERROR = "error";
public static final String SNIPPET_TYPE = "snippet_type";
public static final String SNIPPET_URI = "snippet_uri";
public static final String ARCHIVED = "archived";
public static final String STATUS = "status";
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
public static final String READ_RECEIPT_COUNT = "read_receipt_count";
public static final String EXPIRES_IN = "expires_in";
public static final String LAST_SEEN = "last_seen";
public static final String HAS_SENT = "has_sent";
public static final String IS_PINNED = "is_pinned";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
2017-09-16 07:38:53 +02:00
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " +
SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " +
TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " +
ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " +
2017-09-16 07:38:53 +02:00
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " +
LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);";
2011-12-20 19:20:44 +01:00
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
};
private static final String[] THREAD_PROJECTION = {
ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE,
SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT, IS_PINNED
};
private static final List<String> TYPED_THREAD_PROJECTION = Stream.of(THREAD_PROJECTION)
.map(columnName -> TABLE_NAME + "." + columnName)
.toList();
private static final List<String> COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION = Stream.concat(Stream.concat(Stream.of(TYPED_THREAD_PROJECTION),
Stream.of(RecipientDatabase.TYPED_RECIPIENT_PROJECTION)),
Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION))
.toList();
public static String getCreatePinnedCommand() {
return "ALTER TABLE "+ TABLE_NAME + " " +
"ADD COLUMN " + IS_PINNED + " INTEGER DEFAULT 0;";
}
public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
2011-12-20 19:20:44 +01:00
super(context, databaseHelper);
}
private long createThreadForRecipient(Address address, boolean group, int distributionType) {
2011-12-20 19:20:44 +01:00
ContentValues contentValues = new ContentValues(4);
long date = System.currentTimeMillis();
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
contentValues.put(DATE, date - date % 1000);
contentValues.put(ADDRESS, address.serialize());
2012-09-10 01:10:46 +02:00
if (group)
2013-04-26 03:59:49 +02:00
contentValues.put(TYPE, distributionType);
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
contentValues.put(MESSAGE_COUNT, 0);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
return db.insert(TABLE_NAME, null, contentValues);
}
private void updateThread(long threadId, long count, String body, @Nullable Uri attachment,
2017-09-16 07:38:53 +02:00
long date, int status, int deliveryReceiptCount, long type, boolean unarchive,
long expiresIn, int readReceiptCount)
{
ContentValues contentValues = new ContentValues(7);
2011-12-20 19:20:44 +01:00
contentValues.put(DATE, date - date % 1000);
contentValues.put(MESSAGE_COUNT, count);
2019-07-19 05:23:19 +02:00
if (!body.isEmpty()) {
contentValues.put(SNIPPET, body);
}
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type);
contentValues.put(STATUS, status);
2017-09-16 07:38:53 +02:00
contentValues.put(DELIVERY_RECEIPT_COUNT, deliveryReceiptCount);
contentValues.put(READ_RECEIPT_COUNT, readReceiptCount);
contentValues.put(EXPIRES_IN, expiresIn);
2012-09-10 01:10:46 +02:00
if (unarchive) {
contentValues.put(ARCHIVED, 0);
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
notifyConversationListListeners();
2011-12-20 19:20:44 +01:00
}
2012-09-10 01:10:46 +02:00
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {
ContentValues contentValues = new ContentValues(4);
contentValues.put(DATE, date - date % 1000);
2019-07-19 05:23:19 +02:00
if (!snippet.isEmpty()) {
contentValues.put(SNIPPET, snippet);
}
contentValues.put(SNIPPET_TYPE, type);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
if (unarchive) {
contentValues.put(ARCHIVED, 0);
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
notifyConversationListListeners();
}
2011-12-20 19:20:44 +01:00
private void deleteThread(long threadId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId + ""});
addressCache.remove(threadId);
2011-12-20 19:20:44 +01:00
notifyConversationListListeners();
}
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
private void deleteThreads(Set<Long> threadIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String where = "";
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
for (long threadId : threadIds) {
where += ID + " = '" + threadId + "' OR ";
}
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
where = where.substring(0, where.length() - 4);
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
db.delete(TABLE_NAME, where, null);
for (long threadId: threadIds) {
addressCache.remove(threadId);
}
2011-12-20 19:20:44 +01:00
notifyConversationListListeners();
}
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
private void deleteAllThreads() {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
2012-09-10 01:10:46 +02:00
db.delete(TABLE_NAME, null, null);
addressCache.clear();
2012-09-10 01:10:46 +02:00
notifyConversationListListeners();
2011-12-20 19:20:44 +01:00
}
2012-09-10 01:10:46 +02:00
public void trimAllThreads(int length, ProgressListener listener) {
Cursor cursor = null;
int threadCount = 0;
int complete = 0;
try {
cursor = this.getConversationList();
if (cursor != null)
threadCount = cursor.getCount();
while (cursor != null && cursor.moveToNext()) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
trimThread(threadId, length);
listener.onProgress(++complete, threadCount);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void trimThread(long threadId, int length) {
2018-08-02 15:25:33 +02:00
Log.i("ThreadDatabase", "Trimming thread: " + threadId + " to: " + length);
Cursor cursor = null;
try {
cursor = DatabaseComponent.get(context).mmsSmsDatabase().getConversation(threadId, true);
if (cursor != null && length > 0 && cursor.getCount() > length) {
Log.w("ThreadDatabase", "Cursor count is greater than length!");
cursor.moveToPosition(length - 1);
long lastTweetDate = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.NORMALIZED_DATE_RECEIVED));
2018-08-02 15:25:33 +02:00
Log.i("ThreadDatabase", "Cut off tweet date: " + lastTweetDate);
DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
update(threadId, false);
notifyConversationListeners(threadId);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void trimThreadBefore(long threadId, long timestamp) {
Log.i("ThreadDatabase", "Trimming thread: " + threadId + " before :"+timestamp);
DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp);
DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp);
update(threadId, false);
notifyConversationListeners(threadId);
}
public List<MarkedMessageInfo> setRead(long threadId, boolean lastSeen) {
2011-12-20 19:20:44 +01:00
ContentValues contentValues = new ContentValues(1);
contentValues.put(READ, 1);
contentValues.put(UNREAD_COUNT, 0);
2011-12-20 19:20:44 +01:00
if (lastSeen) {
contentValues.put(LAST_SEEN, System.currentTimeMillis());
}
2011-12-20 19:20:44 +01:00
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""});
2012-09-10 01:10:46 +02:00
final List<MarkedMessageInfo> smsRecords = DatabaseComponent.get(context).smsDatabase().setMessagesRead(threadId);
final List<MarkedMessageInfo> mmsRecords = DatabaseComponent.get(context).mmsDatabase().setMessagesRead(threadId);
2011-12-20 19:20:44 +01:00
notifyConversationListListeners();
return new LinkedList<MarkedMessageInfo>() {{
addAll(smsRecords);
addAll(mmsRecords);
}};
2011-12-20 19:20:44 +01:00
}
2012-09-10 01:10:46 +02:00
public void incrementUnread(long threadId, int amount) {
2013-04-26 03:59:49 +02:00
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.execSQL("UPDATE " + TABLE_NAME + " SET " + READ + " = 0, " +
UNREAD_COUNT + " = " + UNREAD_COUNT + " + ? WHERE " + ID + " = ?",
new String[] {String.valueOf(amount),
String.valueOf(threadId)});
2013-04-26 03:59:49 +02:00
}
public void decrementUnread(long threadId, int amount) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.execSQL("UPDATE " + TABLE_NAME + " SET " + READ + " = 0, " +
UNREAD_COUNT + " = " + UNREAD_COUNT + " - ? WHERE " + ID + " = ? AND " + UNREAD_COUNT + " > 0",
new String[] {String.valueOf(amount),
String.valueOf(threadId)});
}
2013-04-26 03:59:49 +02:00
public void setDistributionType(long threadId, int distributionType) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(TYPE, distributionType);
2011-12-20 19:20:44 +01:00
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
2012-09-10 01:10:46 +02:00
notifyConversationListListeners();
2011-12-20 19:20:44 +01:00
}
2012-09-10 01:10:46 +02:00
public int getDistributionType(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[]{TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
}
return DistributionTypes.DEFAULT;
} finally {
if (cursor != null) cursor.close();
}
}
Add a global search (#834) * feat: modifying search functionalities to include contacts * feat: add global search UI input layouts and color attributes * feat: add global search repository and model content * feat: adding diff callbacks and wiring up global search vm to views * feat: adding scroll to message, figuring out new query for recipient thread search * feat: messing with the search and highlighting functionality after wiring up bindings * fix: compile error from merge * fix: gradlew build errors * feat: filtering contacts by existing un-archived threads * refactor: prevent note to self breaking, update queries and logic in search repo to include member->group reverse searches * feat: adding home screen new redesigns for search * feat: replacing designs and adding new group subtitle text * feat: small design improvements and incrementing gradle build number to install on device * feat: add scrollbars for search * feat: replace isVisible for cancel button now that GlobalSearchInputLayout.kt replaces header * refactor: all queries are debounced not just all but 2 char * refactor: remove visibility modifiers for cancel icon * refactor: use simplified non-db and context related models in display, remove db get group members call from binding data * fix: use threadId instead of group's address * refactor: better close on cancel, removing only yourself from group member list in open groups * refactor: seed view back to inflated on create and visibility for empty placeholder and seed view text * refactor: fixing build issues and new designs for message list * refactor: use dynamic limit * refactor: include raw session ID string search for non-empty threads * fix: build lint errors * fix: build issues * feat: add in path to the settings activity * refactor: remove wildcard imports
2022-02-07 07:06:27 +01:00
public Cursor searchConversationAddresses(String addressQuery) {
if (addressQuery == null || addressQuery.isEmpty()) {
return null;
}
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String selection = TABLE_NAME + "." + ADDRESS + " LIKE ? AND " + TABLE_NAME + "." + MESSAGE_COUNT + " != 0";
String[] selectionArgs = new String[]{addressQuery+"%"};
String query = createQuery(selection, 0);
Cursor cursor = db.rawQuery(query, selectionArgs);
return cursor;
}
public Cursor getFilteredConversationList(@Nullable List<Address> filter) {
2011-12-20 19:20:44 +01:00
if (filter == null || filter.size() == 0)
return null;
2012-09-10 01:10:46 +02:00
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<List<Address>> partitionedAddresses = Util.partition(filter, 900);
List<Cursor> cursors = new LinkedList<>();
2012-09-10 01:10:46 +02:00
for (List<Address> addresses : partitionedAddresses) {
String selection = TABLE_NAME + "." + ADDRESS + " = ?";
String[] selectionArgs = new String[addresses.size()];
2012-09-10 01:10:46 +02:00
for (int i=0;i<addresses.size()-1;i++)
selection += (" OR " + TABLE_NAME + "." + ADDRESS + " = ?");
int i= 0;
for (Address address : addresses) {
selectionArgs[i++] = DelimiterUtil.escape(address.serialize(), ' ');
}
2017-11-17 00:21:46 +01:00
String query = createQuery(selection, 0);
cursors.add(db.rawQuery(query, selectionArgs));
2011-12-20 19:20:44 +01:00
}
2012-09-10 01:10:46 +02:00
Cursor cursor = cursors.size() > 1 ? new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) : cursors.get(0);
2012-09-10 01:10:46 +02:00
setNotifyConverationListListeners(cursor);
2011-12-20 19:20:44 +01:00
return cursor;
}
2012-09-10 01:10:46 +02:00
2017-11-17 00:21:46 +01:00
public Cursor getRecentConversationList(int limit) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(MESSAGE_COUNT + " != 0", limit);
return db.rawQuery(query, null);
}
public int getUnapprovedConversationCount() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
String query = "SELECT COUNT (*) FROM " + TABLE_NAME +
" LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS +
" LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GROUP_ID +
" WHERE " + MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " + MESSAGE_COUNT + " = " + UNREAD_COUNT + " AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " +
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
cursor = db.rawQuery(query, null);
if (cursor != null && cursor.moveToFirst())
return cursor.getInt(0);
} finally {
if (cursor != null)
cursor.close();
}
return 0;
}
public long getLatestUnapprovedConversationTimestamp() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
String where = "SELECT " + DATE + " FROM " + TABLE_NAME +
" LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS +
" LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GROUP_ID +
" WHERE " + MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " +
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL ORDER BY " + DATE + " DESC LIMIT 1";
cursor = db.rawQuery(where, null);
if (cursor != null && cursor.moveToFirst())
return cursor.getLong(0);
} finally {
if (cursor != null)
cursor.close();
}
return 0;
}
2012-09-10 01:10:46 +02:00
public Cursor getConversationList() {
String where = "(" + MESSAGE_COUNT + " != 0 OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " +
"AND " + ARCHIVED + " = 0 ";
return getConversationList(where);
}
Add Session Id blinding (#862) * feat: Add Session Id blinding Including modified version of lazysodium-android to expose missing libsodium functions, we could build from a fork which we still need to setup. * Add v4 onion request handling * Update SOGS signature construction * Fix SOGS signature construction * Update onion request * Update signature data * Keep path prefixes for v4 endpoints * Update SOGS signature message * Rename to remove api version suffix * Update onion response parsing * Refactor file download paths * Implement request batching * Refactor batch response handling * Handle batch endpoint responses * Update batch endpoint responses * Update attachment download handling * Handle file downloads * Handle inbox messages * Fix issue with file downloads * Preserve image bytearray encoding * Refactor * Open group message requests * Check id blinding in user detail bottom sheet rather * Message validation refactor * Cache last inbox/outbox server ids * Update message encryption/decryption * Refactor * Refactor * Bypass user details bottom sheet in open groups for blinded session ids * Fix capabilities call auth * Refactor * Revert default server details * Update sodium dependency to forked repo * Fix attachment upload * Revert "Update sodium dependency to forked repo" This reverts commit c7db9529f900d09585ab94e440f6645faa88544e. * Add signed sodium lib * Update contact id truncation and mention logic * Open group inbox messaging fix * Refactor * Update blinded id check * Fix open group message sends * Fix crash on open group direct message send * Direct message refactor * Direct message encrypt/decrypt fixes * Use updated curve25519 version * Updated lazysodium dependency * Update encryption/decryption calls * Handle direct message parse errors * Minor refactor * Existing chat refactor * Update encryption & decryption parameters * Fix authenticated ciphertext size * Set direct message sync target * Update direct message thread lookup * Add blinded id mapping table * Add blinded id mapping table * Update threads after sends * Update open group message timestamp handling * Filter unblinded contacts * Format blinded id mentions * Add message deleted field * Hide open group inbox id * Update message request response handling * Update message request response sender handling * Fix mentions of blinded ids * Handle open group poll failure * fix: add log for failed open group onion request, add decoding body for blinding required error at destination * fix: change the error check * Persist group members * Reschedule polling after capabilities update * Retry on other exceptions * Minor refactor * Open group profile fix * Group member db schema update * Fix ban request key * Update ban response type * Ban endpoint updates * Ban endpoint updates * Delete messages Co-authored-by: charles <charles@oxen.io> Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-08-10 10:17:48 +02:00
public Cursor getBlindedConversationList() {
String where = TABLE_NAME + "." + ADDRESS + " LIKE '" + IdPrefix.BLINDED.getValue() + "%' ";
return getConversationList(where);
}
public Cursor getApprovedConversationList() {
String where = "((" + MESSAGE_COUNT + " != 0 AND (" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+CLOSED_GROUP_PREFIX+"%')) OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " +
"AND " + ARCHIVED + " = 0 ";
return getConversationList(where);
}
public Cursor getUnapprovedConversationList() {
String where = MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
return getConversationList(where);
}
public Cursor getArchivedConversationList() {
String where = "(" + MESSAGE_COUNT + " != 0 OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " +
"AND " + ARCHIVED + " = 1 ";
return getConversationList(where);
}
private Cursor getConversationList(String where) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(where, 0);
Cursor cursor = db.rawQuery(query, null);
2011-12-20 19:20:44 +01:00
setNotifyConverationListListeners(cursor);
2011-12-20 19:20:44 +01:00
return cursor;
}
2012-09-10 01:10:46 +02:00
public Cursor getDirectShareList() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
2017-11-17 00:21:46 +01:00
String query = createQuery(MESSAGE_COUNT + " != 0", 0);
return db.rawQuery(query, null);
}
Paged conversation recycler, update compile sdk version 31 (#1049) * Update build tools * Update appcompat version * Update dependencies * feat: add paging into conversation recycler and queries to fetch data off-thread * refactor: wip for updating paged results and bucketing messages / fetching enough to display * fix: currently works for scrolling and possibly refreshing? need scroll to message and auto scroll down on insert (at bottom) * fix: search and scrolling to X message works now * build: increase version code and name * fix: re-add refresh, remove the outdated comment * refactor: lets see if 25 size pages increases performance :eyes: * feat: add in some equals overrides for mms records to refresh if media has finished DLing * feat: add scroll to bottom for new messages if we are at the end of the chat * build: update build numbers * fix: update AGP and fix compile errors for sdk version 31 * feat: add log for loki-avatar and loki-fs on upload types and responses * feat: increase build number to match latest installed version * feat: changing props and permission checks for call service * fix: possible service exception when no call ID remote foreground service not terminated * revert: google services version * fix: re-add paging dependency * feat: adding new last seen function and figuring out the last seen for recycler adapter * build: update version names and codes for deploy * refactor: undo the new adapter and query changes to use previous cursor logic. revert this commit to enable new paged adapter * fix: use author's address in typist equality and hashcode for set inclusion * refactor: refactor the select contacts activity * refactor: refactor the select contacts activity * build: update version code * fix: hide all other bound views if deleted * refactor: change voice message tint, upgrade build number * fix: message detail showing up properly * revert: realise copy public key is actually not allowed if open group participant * fix: copy session ID, message detail activity support re-enabled * build: update build version code * build: remove version name * build: update build code * feat: google services version minimum compatible * fix: selection for re-created objects not properly highlighting * fix: foreground CENTER_INSIDE instead of just CENTER for scaletype * build: update version code * fix: don't show error if no error * build: update version code * fix: clear error messages if any on successful send Co-authored-by: charles <charles@oxen.io>
2022-12-19 01:29:05 +01:00
public void setLastSeen(long threadId, long timestamp) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
Paged conversation recycler, update compile sdk version 31 (#1049) * Update build tools * Update appcompat version * Update dependencies * feat: add paging into conversation recycler and queries to fetch data off-thread * refactor: wip for updating paged results and bucketing messages / fetching enough to display * fix: currently works for scrolling and possibly refreshing? need scroll to message and auto scroll down on insert (at bottom) * fix: search and scrolling to X message works now * build: increase version code and name * fix: re-add refresh, remove the outdated comment * refactor: lets see if 25 size pages increases performance :eyes: * feat: add in some equals overrides for mms records to refresh if media has finished DLing * feat: add scroll to bottom for new messages if we are at the end of the chat * build: update build numbers * fix: update AGP and fix compile errors for sdk version 31 * feat: add log for loki-avatar and loki-fs on upload types and responses * feat: increase build number to match latest installed version * feat: changing props and permission checks for call service * fix: possible service exception when no call ID remote foreground service not terminated * revert: google services version * fix: re-add paging dependency * feat: adding new last seen function and figuring out the last seen for recycler adapter * build: update version names and codes for deploy * refactor: undo the new adapter and query changes to use previous cursor logic. revert this commit to enable new paged adapter * fix: use author's address in typist equality and hashcode for set inclusion * refactor: refactor the select contacts activity * refactor: refactor the select contacts activity * build: update version code * fix: hide all other bound views if deleted * refactor: change voice message tint, upgrade build number * fix: message detail showing up properly * revert: realise copy public key is actually not allowed if open group participant * fix: copy session ID, message detail activity support re-enabled * build: update build version code * build: remove version name * build: update build code * feat: google services version minimum compatible * fix: selection for re-created objects not properly highlighting * fix: foreground CENTER_INSIDE instead of just CENTER for scaletype * build: update version code * fix: don't show error if no error * build: update version code * fix: clear error messages if any on successful send Co-authored-by: charles <charles@oxen.io>
2022-12-19 01:29:05 +01:00
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();
}
Paged conversation recycler, update compile sdk version 31 (#1049) * Update build tools * Update appcompat version * Update dependencies * feat: add paging into conversation recycler and queries to fetch data off-thread * refactor: wip for updating paged results and bucketing messages / fetching enough to display * fix: currently works for scrolling and possibly refreshing? need scroll to message and auto scroll down on insert (at bottom) * fix: search and scrolling to X message works now * build: increase version code and name * fix: re-add refresh, remove the outdated comment * refactor: lets see if 25 size pages increases performance :eyes: * feat: add in some equals overrides for mms records to refresh if media has finished DLing * feat: add scroll to bottom for new messages if we are at the end of the chat * build: update build numbers * fix: update AGP and fix compile errors for sdk version 31 * feat: add log for loki-avatar and loki-fs on upload types and responses * feat: increase build number to match latest installed version * feat: changing props and permission checks for call service * fix: possible service exception when no call ID remote foreground service not terminated * revert: google services version * fix: re-add paging dependency * feat: adding new last seen function and figuring out the last seen for recycler adapter * build: update version names and codes for deploy * refactor: undo the new adapter and query changes to use previous cursor logic. revert this commit to enable new paged adapter * fix: use author's address in typist equality and hashcode for set inclusion * refactor: refactor the select contacts activity * refactor: refactor the select contacts activity * build: update version code * fix: hide all other bound views if deleted * refactor: change voice message tint, upgrade build number * fix: message detail showing up properly * revert: realise copy public key is actually not allowed if open group participant * fix: copy session ID, message detail activity support re-enabled * build: update build version code * build: remove version name * build: update build code * feat: google services version minimum compatible * fix: selection for re-created objects not properly highlighting * fix: foreground CENTER_INSIDE instead of just CENTER for scaletype * build: update version code * fix: don't show error if no error * build: update version code * fix: clear error messages if any on successful send Co-authored-by: charles <charles@oxen.io>
2022-12-19 01:29:05 +01:00
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);
try {
if (cursor != null && cursor.moveToFirst()) {
return new Pair<>(cursor.getLong(0), cursor.getLong(1) == 1);
}
return new Pair<>(-1L, false);
} finally {
if (cursor != null) cursor.close();
}
}
2021-05-21 03:09:03 +02:00
public Long getLastUpdated(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[]{DATE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(0);
}
return -1L;
} finally {
if (cursor != null) cursor.close();
}
}
public int getMessageCount(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] columns = new String[]{MESSAGE_COUNT};
String[] args = new String[]{String.valueOf(threadId)};
try (Cursor cursor = db.query(TABLE_NAME, columns, ID_WHERE, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0);
}
return 0;
}
}
2011-12-20 19:20:44 +01:00
public void deleteConversation(long threadId) {
DatabaseComponent.get(context).smsDatabase().deleteThread(threadId);
DatabaseComponent.get(context).mmsDatabase().deleteThread(threadId);
DatabaseComponent.get(context).draftDatabase().clearDrafts(threadId);
DatabaseComponent.get(context).lokiMessageDatabase().deleteThread(threadId);
2011-12-20 19:20:44 +01:00
deleteThread(threadId);
notifyConversationListeners(threadId);
notifyConversationListListeners();
SessionMetaProtocol.clearReceivedMessages();
2012-09-10 01:10:46 +02:00
}
Add one on one calls over clearnet (#864) * feat: adding basic webrtc deps and test activity * more testing code * feat: add protos and bump version * feat: added basic call functionality * feat: adding UI and flipping cameras * feat: add stats and starting call bottom sheet * feat: hanging up and bottom sheet behaviors should work now * feat: add call stats report on frontend * feat: add relay toggle for answer and offer * fix: add keep screen on and more end call message on back pressed / on finish * refactor: removing and replacing dagger 1 dep with android hilt * feat: include latest proto * feat: update to utilise call ID * feat: add stun and turn * refactor: playing around with deps and transport types * feat: adding call service functionality and permissions for calls * feat: add call manager and more static intent building functions for WebRtcCallService.kt * feat: adding ringers and more audio boilerplate * feat: audio manager call service boilerplate * feat: update kotlin and add in call view model and more management functions * refactor: moving call code around to service and viewmodel interactions * feat: plugging CallManager.kt into view model and service, fixing up dependencies * feat: implementing more WebRtcCallService.kt functions and handlers for actions as well as lifecycle * feat: adding more lifecycle vm and callmanager / call service functionality * feat: adding more command handlers in WebRtcCallService.kt * feat: more commands handled, adding lock manager and bluetooth permissions * feat: adding remainder of basic functionality to services and CallManager.kt * feat: hooking up calls and fixing broken dependencies and compile errors * fix: add timestamp to incoming call * feat: some connection and service launching / ring lifecycle * feat: call establishing and displaying * fix: fixing call connect flows * feat: ringers and better state handling * feat: updating call layout * feat: add fixes to bluetooth and begin the network renegotiation * feat: add call related permissions and more network handover tests * fix: don't display call option in conversation and don't show notification if option not enabled * fix: incoming ringer fix on receiving call, call notification priorities and notification channel update * build: update build number for testing * fix: bluetooth auto-connection and re-connection fixes, removing finished todos, allowing self-send call messages for deduping answers * feat: add pre-offer information and action handling in web rtc call service * refactor: discard offer messages from non-matching pre-offers we are already expecting * build: build numbers and version name update * feat: handle discarding pending calls from linked devices * feat: add signing props to release config build * docs: fix comment on time being 300s (5m) instead of 30s * feat: adding call messages for incoming/outgoing/missed * refactor: handle in-thread call notifications better and replace deny button intent with denyCallIntent instead of hangup * feat: add a hangup via data channel message * feat: process microphone enabled events and remove debuggable from build.gradle * feat: add first call notification * refactor: set the buttons to match iOS in terms of enable disable and colours * refactor: change the call logos in control messages * refactor: more bluetooth improvements * refactor: move start ringer and init of audio manager to CallManager.kt and string fix up * build: remove debuggable for release build * refactor: replace call icons * feat: adding a call time display * refactor: change the call time to update every second * refactor: testing out the full screen intents * refactor: wrapper use corrected session description, set title to recipient displayName, indicate session calls * fix: crash on view with a parent already attached * refactor: aspect ratio fit preserved * refactor: add wantsToAnswer ability in pre-init for fullscreenintent * refactor: prevent calls from non hasSent participants * build: update gradle code * refactor: replace timeout schedule with a seconds count * fix: various bug fixes for calls * fix: remove end call from busy * refactor: use answerCall instead of manual intent building again * build: new version * feat: add silenced notifications for call notification builder. check pre-offer and connecting state for pending connection * build: update build number * fix: text color uses overridden style value * fix: remove wrap content for renderers and look more at recovering from network switches * build: update build number * refactor: remove whitespace * build: update build number * refactor: used shared number for BatchMessageReceiveJob.kt parameter across pollers * fix: glide in update crash * fix: bug fixes for self-send answer / hangup messages * build: update build number * build: update build.gradle number * refactor: compile errors and refactoring to view binding * fix: set the content to binding.root view * build: increase build number * build: update build numbers * feat: adding base for rotation and picking random subset of turn servers * feat: starting the screen rotation processing * feat: setting up rotation for the remote render view * refactor: applying rotation and mirroring based on front / rear cameras that wraps nicely, only scale reworking needed * refactor: calls video stretching but consistent * refactor: state machine and tests for the transition events * feat: new call state processing * refactor: adding reconnecting logic and visuals * feat: state machine reconnect logic wip * feat: add reconnecting and merge fixes * feat: check new session based off current state * feat: reconnection logic works correctly now * refactor: reduce TIMEOUT_SECONDS to 30 from 90 * feat: reset peer connection on DC to prevent ICE messages from old connection or stale state in reconnecting * refactor: add null case * fix: set approved on new outgoing threads, use approved more deeply and invalidate the options menu on recipient modified. Add approvedMe flag toggles for visible message receive * fix: add name update in action bar on modified, change where approvedMe is set * build: increment build number * build: update build number * fix: merge compile errors and increment build number * refactor: remove negotiation based on which party dropped connection * refactor: call reconnection improvement tested cross platform to re-establish * refactor: failed and disconnect events only handled if either the reconnect or the timeout runnables are not set * build: update version number * fix: reduce timeout * fix: fixes the incoming hangup logic for linked devices * refactor: match iOS styling for call activity closer * chore: upgrade build numbers * feat: add in call settings dialog for if calls is disabled in conversation * feat: add a first call missed control message and info popup with link to privacy settings * fix: looking at crash for specific large transaction in NotificationManager * refactor: removing the people in case transaction size reduces to fix notif crash * fix: comment out the entire send multiple to see if it fixes the issue * refactor: revert to including the full notification process in a try/catch to handle weird responses from NotificationManager * fix: add in notification settings prompt for calls and try to fall back to dirty full screen intent / start activity if we're allowed * build: upgrade build number
2022-04-19 06:25:40 +02:00
public long getThreadIdIfExistsFor(String address) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESS + " = ?";
Add one on one calls over clearnet (#864) * feat: adding basic webrtc deps and test activity * more testing code * feat: add protos and bump version * feat: added basic call functionality * feat: adding UI and flipping cameras * feat: add stats and starting call bottom sheet * feat: hanging up and bottom sheet behaviors should work now * feat: add call stats report on frontend * feat: add relay toggle for answer and offer * fix: add keep screen on and more end call message on back pressed / on finish * refactor: removing and replacing dagger 1 dep with android hilt * feat: include latest proto * feat: update to utilise call ID * feat: add stun and turn * refactor: playing around with deps and transport types * feat: adding call service functionality and permissions for calls * feat: add call manager and more static intent building functions for WebRtcCallService.kt * feat: adding ringers and more audio boilerplate * feat: audio manager call service boilerplate * feat: update kotlin and add in call view model and more management functions * refactor: moving call code around to service and viewmodel interactions * feat: plugging CallManager.kt into view model and service, fixing up dependencies * feat: implementing more WebRtcCallService.kt functions and handlers for actions as well as lifecycle * feat: adding more lifecycle vm and callmanager / call service functionality * feat: adding more command handlers in WebRtcCallService.kt * feat: more commands handled, adding lock manager and bluetooth permissions * feat: adding remainder of basic functionality to services and CallManager.kt * feat: hooking up calls and fixing broken dependencies and compile errors * fix: add timestamp to incoming call * feat: some connection and service launching / ring lifecycle * feat: call establishing and displaying * fix: fixing call connect flows * feat: ringers and better state handling * feat: updating call layout * feat: add fixes to bluetooth and begin the network renegotiation * feat: add call related permissions and more network handover tests * fix: don't display call option in conversation and don't show notification if option not enabled * fix: incoming ringer fix on receiving call, call notification priorities and notification channel update * build: update build number for testing * fix: bluetooth auto-connection and re-connection fixes, removing finished todos, allowing self-send call messages for deduping answers * feat: add pre-offer information and action handling in web rtc call service * refactor: discard offer messages from non-matching pre-offers we are already expecting * build: build numbers and version name update * feat: handle discarding pending calls from linked devices * feat: add signing props to release config build * docs: fix comment on time being 300s (5m) instead of 30s * feat: adding call messages for incoming/outgoing/missed * refactor: handle in-thread call notifications better and replace deny button intent with denyCallIntent instead of hangup * feat: add a hangup via data channel message * feat: process microphone enabled events and remove debuggable from build.gradle * feat: add first call notification * refactor: set the buttons to match iOS in terms of enable disable and colours * refactor: change the call logos in control messages * refactor: more bluetooth improvements * refactor: move start ringer and init of audio manager to CallManager.kt and string fix up * build: remove debuggable for release build * refactor: replace call icons * feat: adding a call time display * refactor: change the call time to update every second * refactor: testing out the full screen intents * refactor: wrapper use corrected session description, set title to recipient displayName, indicate session calls * fix: crash on view with a parent already attached * refactor: aspect ratio fit preserved * refactor: add wantsToAnswer ability in pre-init for fullscreenintent * refactor: prevent calls from non hasSent participants * build: update gradle code * refactor: replace timeout schedule with a seconds count * fix: various bug fixes for calls * fix: remove end call from busy * refactor: use answerCall instead of manual intent building again * build: new version * feat: add silenced notifications for call notification builder. check pre-offer and connecting state for pending connection * build: update build number * fix: text color uses overridden style value * fix: remove wrap content for renderers and look more at recovering from network switches * build: update build number * refactor: remove whitespace * build: update build number * refactor: used shared number for BatchMessageReceiveJob.kt parameter across pollers * fix: glide in update crash * fix: bug fixes for self-send answer / hangup messages * build: update build number * build: update build.gradle number * refactor: compile errors and refactoring to view binding * fix: set the content to binding.root view * build: increase build number * build: update build numbers * feat: adding base for rotation and picking random subset of turn servers * feat: starting the screen rotation processing * feat: setting up rotation for the remote render view * refactor: applying rotation and mirroring based on front / rear cameras that wraps nicely, only scale reworking needed * refactor: calls video stretching but consistent * refactor: state machine and tests for the transition events * feat: new call state processing * refactor: adding reconnecting logic and visuals * feat: state machine reconnect logic wip * feat: add reconnecting and merge fixes * feat: check new session based off current state * feat: reconnection logic works correctly now * refactor: reduce TIMEOUT_SECONDS to 30 from 90 * feat: reset peer connection on DC to prevent ICE messages from old connection or stale state in reconnecting * refactor: add null case * fix: set approved on new outgoing threads, use approved more deeply and invalidate the options menu on recipient modified. Add approvedMe flag toggles for visible message receive * fix: add name update in action bar on modified, change where approvedMe is set * build: increment build number * build: update build number * fix: merge compile errors and increment build number * refactor: remove negotiation based on which party dropped connection * refactor: call reconnection improvement tested cross platform to re-establish * refactor: failed and disconnect events only handled if either the reconnect or the timeout runnables are not set * build: update version number * fix: reduce timeout * fix: fixes the incoming hangup logic for linked devices * refactor: match iOS styling for call activity closer * chore: upgrade build numbers * feat: add in call settings dialog for if calls is disabled in conversation * feat: add a first call missed control message and info popup with link to privacy settings * fix: looking at crash for specific large transaction in NotificationManager * refactor: removing the people in case transaction size reduces to fix notif crash * fix: comment out the entire send multiple to see if it fixes the issue * refactor: revert to including the full notification process in a try/catch to handle weird responses from NotificationManager * fix: add in notification settings prompt for calls and try to fall back to dirty full screen intent / start activity if we're allowed * build: upgrade build number
2022-04-19 06:25:40 +02:00
String[] recipientsArg = new String[] {address};
Cursor cursor = null;
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
if (cursor != null && cursor.moveToFirst())
2012-10-01 04:56:29 +02:00
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
2011-12-20 19:20:44 +01:00
else
2012-10-01 04:56:29 +02:00
return -1L;
2011-12-20 19:20:44 +01:00
} finally {
if (cursor != null)
2012-10-01 04:56:29 +02:00
cursor.close();
2011-12-20 19:20:44 +01:00
}
}
2012-09-10 01:10:46 +02:00
Add one on one calls over clearnet (#864) * feat: adding basic webrtc deps and test activity * more testing code * feat: add protos and bump version * feat: added basic call functionality * feat: adding UI and flipping cameras * feat: add stats and starting call bottom sheet * feat: hanging up and bottom sheet behaviors should work now * feat: add call stats report on frontend * feat: add relay toggle for answer and offer * fix: add keep screen on and more end call message on back pressed / on finish * refactor: removing and replacing dagger 1 dep with android hilt * feat: include latest proto * feat: update to utilise call ID * feat: add stun and turn * refactor: playing around with deps and transport types * feat: adding call service functionality and permissions for calls * feat: add call manager and more static intent building functions for WebRtcCallService.kt * feat: adding ringers and more audio boilerplate * feat: audio manager call service boilerplate * feat: update kotlin and add in call view model and more management functions * refactor: moving call code around to service and viewmodel interactions * feat: plugging CallManager.kt into view model and service, fixing up dependencies * feat: implementing more WebRtcCallService.kt functions and handlers for actions as well as lifecycle * feat: adding more lifecycle vm and callmanager / call service functionality * feat: adding more command handlers in WebRtcCallService.kt * feat: more commands handled, adding lock manager and bluetooth permissions * feat: adding remainder of basic functionality to services and CallManager.kt * feat: hooking up calls and fixing broken dependencies and compile errors * fix: add timestamp to incoming call * feat: some connection and service launching / ring lifecycle * feat: call establishing and displaying * fix: fixing call connect flows * feat: ringers and better state handling * feat: updating call layout * feat: add fixes to bluetooth and begin the network renegotiation * feat: add call related permissions and more network handover tests * fix: don't display call option in conversation and don't show notification if option not enabled * fix: incoming ringer fix on receiving call, call notification priorities and notification channel update * build: update build number for testing * fix: bluetooth auto-connection and re-connection fixes, removing finished todos, allowing self-send call messages for deduping answers * feat: add pre-offer information and action handling in web rtc call service * refactor: discard offer messages from non-matching pre-offers we are already expecting * build: build numbers and version name update * feat: handle discarding pending calls from linked devices * feat: add signing props to release config build * docs: fix comment on time being 300s (5m) instead of 30s * feat: adding call messages for incoming/outgoing/missed * refactor: handle in-thread call notifications better and replace deny button intent with denyCallIntent instead of hangup * feat: add a hangup via data channel message * feat: process microphone enabled events and remove debuggable from build.gradle * feat: add first call notification * refactor: set the buttons to match iOS in terms of enable disable and colours * refactor: change the call logos in control messages * refactor: more bluetooth improvements * refactor: move start ringer and init of audio manager to CallManager.kt and string fix up * build: remove debuggable for release build * refactor: replace call icons * feat: adding a call time display * refactor: change the call time to update every second * refactor: testing out the full screen intents * refactor: wrapper use corrected session description, set title to recipient displayName, indicate session calls * fix: crash on view with a parent already attached * refactor: aspect ratio fit preserved * refactor: add wantsToAnswer ability in pre-init for fullscreenintent * refactor: prevent calls from non hasSent participants * build: update gradle code * refactor: replace timeout schedule with a seconds count * fix: various bug fixes for calls * fix: remove end call from busy * refactor: use answerCall instead of manual intent building again * build: new version * feat: add silenced notifications for call notification builder. check pre-offer and connecting state for pending connection * build: update build number * fix: text color uses overridden style value * fix: remove wrap content for renderers and look more at recovering from network switches * build: update build number * refactor: remove whitespace * build: update build number * refactor: used shared number for BatchMessageReceiveJob.kt parameter across pollers * fix: glide in update crash * fix: bug fixes for self-send answer / hangup messages * build: update build number * build: update build.gradle number * refactor: compile errors and refactoring to view binding * fix: set the content to binding.root view * build: increase build number * build: update build numbers * feat: adding base for rotation and picking random subset of turn servers * feat: starting the screen rotation processing * feat: setting up rotation for the remote render view * refactor: applying rotation and mirroring based on front / rear cameras that wraps nicely, only scale reworking needed * refactor: calls video stretching but consistent * refactor: state machine and tests for the transition events * feat: new call state processing * refactor: adding reconnecting logic and visuals * feat: state machine reconnect logic wip * feat: add reconnecting and merge fixes * feat: check new session based off current state * feat: reconnection logic works correctly now * refactor: reduce TIMEOUT_SECONDS to 30 from 90 * feat: reset peer connection on DC to prevent ICE messages from old connection or stale state in reconnecting * refactor: add null case * fix: set approved on new outgoing threads, use approved more deeply and invalidate the options menu on recipient modified. Add approvedMe flag toggles for visible message receive * fix: add name update in action bar on modified, change where approvedMe is set * build: increment build number * build: update build number * fix: merge compile errors and increment build number * refactor: remove negotiation based on which party dropped connection * refactor: call reconnection improvement tested cross platform to re-establish * refactor: failed and disconnect events only handled if either the reconnect or the timeout runnables are not set * build: update version number * fix: reduce timeout * fix: fixes the incoming hangup logic for linked devices * refactor: match iOS styling for call activity closer * chore: upgrade build numbers * feat: add in call settings dialog for if calls is disabled in conversation * feat: add a first call missed control message and info popup with link to privacy settings * fix: looking at crash for specific large transaction in NotificationManager * refactor: removing the people in case transaction size reduces to fix notif crash * fix: comment out the entire send multiple to see if it fixes the issue * refactor: revert to including the full notification process in a try/catch to handle weird responses from NotificationManager * fix: add in notification settings prompt for calls and try to fall back to dirty full screen intent / start activity if we're allowed * build: upgrade build number
2022-04-19 06:25:40 +02:00
public long getThreadIdIfExistsFor(Recipient recipient) {
return getThreadIdIfExistsFor(recipient.getAddress().serialize());
}
public long getOrCreateThreadIdFor(Recipient recipient) {
return getOrCreateThreadIdFor(recipient, DistributionTypes.DEFAULT);
2013-04-26 03:59:49 +02:00
}
Performance improvements and bug fixes (#869) * refactor: fail on testSnode instead of recursively using up snode list. add call timeout on http client * refactor: refactoring batch message receives and pollers * refactor: reduce thread utils pool count to a 2 thread fixed pool. Do a check against pubkey instead of room names for oxenHostedOpenGroup * refactor: caching lib with potential loader fixes and no-cache for giphy * refactor: remove store and instead use ConcurrentHashMap with a backing update coroutine * refactor: queue trim thread jobs instead of add every message processed * fix: wrapping auth token and initial sync for open groups in a threadutils queued runnable, getting initial sync times down * fix: fixing the user contacts cache in ConversationAdapter.kt * refactor: improve polling and initial sync, move group joins from config messages into a background job fetching image. * refactor: improving the job queuing for open groups, replacing placeholder avatar generation with a custom glide loader and archiving initial sync of open groups * feat: add OpenGroupDeleteJob.kt * feat: add open group delete job to process deletions after batch adding * feat: add vacuum and fix job queue re-adding jobs forever, only try to set message hash values in DB if they have changed * refactor: remove redundant inflation for profile image views throughout app * refactor(wip): reducing layout inflation and starting to refactor the open group deletion issues taking a long time * refactor(wip): refactoring group deletion to not iterate through and delete messages individually * refactor(wip): refactoring group deletion to not iterate through and delete messages individually * fix: group deletion optimisation * build: bump build number * build: bump build number and fix batch message receive retry logic * fix: clear out open group deletes * fix: update visible ConversationAdapter.kt binding for initial contact fetching and better traces for debugging background jobs * fix: add in check for / force sync latest encryption key pair from linked devices if we already have that closed group * Rename .java to .kt * refactor: change MmsDatabase to kotlin to make list operations easier * fix: nullable type * fix: compilation issues and constants in .kt instead of .java * fix: bug fix expiration timer on closed group recipient * feat: use the job queue properly across executors * feat: start on open group dispatcher-specific logic, probably a queue factory based on openGroupId if that is the same across new message and deletion jobs to ensure consistent entry and removal * refactor: removing redundant code and fixing jobqueue per opengroup * fix: allow attachments in note to self * fix: make the minWidth in quote view bind max of text / title and body, wrapped ? * fix: fixing up layouts and code view layouts * fix: remove TODO, remove timestamp binding * feat: fix view logic, avatars and padding, downloading attachments lazily (on bind), fixing potential crash, add WindowDebouncer.kt * fix: NPE on viewModel recipient from removed thread while tearing down the Recipient observer in ConversationActivityV2.kt * refactor: replace conversation notification debouncer handler with handlerthread, same as conversation list debouncer * refactor: UI for groups and poller improvements * fix: revert some changes in poller * feat: add header back in for message requests * refactor: remove Trace calls, add more conditions to the HomeDiffUtil for updating more efficiently * feat: try update the home adapter if we get a profile picture modified event * feat: bump build numbers * fix: try to start with list in homeViewModel if we don't have already, render quotes to be width of attachment slide view instead of fixed * fix: set channel to be conflated instead of no buffer * fix: set unreads based off last local user message vs incrementing unreads to be all amount * feat: add profile update flag, update build number * fix: link preview thumbnails download on bind * fix: centercrop placeholder in glide request * feat: recycle the contact selection list and profile image in unbind * fix: try to prevent user KP crash at weird times * fix: remove additional log, improve attachment download success rate, fix share logs dialog issue
2022-06-08 09:12:34 +02:00
public void setThreadArchived(long threadId) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(ARCHIVED, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE,
new String[] {String.valueOf(threadId)});
notifyConversationListListeners();
notifyConversationListeners(threadId);
}
public long getOrCreateThreadIdFor(Recipient recipient, int distributionType) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESS + " = ?";
String[] recipientsArg = new String[]{recipient.getAddress().serialize()};
Cursor cursor = null;
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
2012-09-10 01:10:46 +02:00
if (cursor != null && cursor.moveToFirst()) {
2012-10-01 04:56:29 +02:00
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else {
DatabaseComponent.get(context).recipientDatabase().setProfileSharing(recipient, true);
return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType);
}
2011-12-20 19:20:44 +01:00
} finally {
if (cursor != null)
2012-10-01 04:56:29 +02:00
cursor.close();
2011-12-20 19:20:44 +01:00
}
}
2012-09-10 01:10:46 +02:00
public @Nullable Recipient getRecipientForThreadId(long threadId) {
if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) {
return Recipient.from(context, addressCache.get(threadId), false);
}
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
2021-02-17 06:09:36 +01:00
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
addressCache.put(threadId, address);
return Recipient.from(context, address, false);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public void setHasSent(long threadId, boolean hasSent) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(HAS_SENT, hasSent ? 1 : 0);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE,
new String[] {String.valueOf(threadId)});
notifyConversationListeners(threadId);
}
public boolean update(long threadId, boolean unarchive) {
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
2011-12-20 19:20:44 +01:00
long count = mmsSmsDatabase.getConversationCount(threadId);
2012-09-10 01:10:46 +02:00
boolean shouldDeleteEmptyThread = deleteThreadOnEmpty(threadId);
if (count == 0 && shouldDeleteEmptyThread) {
2011-12-20 19:20:44 +01:00
deleteThread(threadId);
notifyConversationListListeners();
return true;
2011-12-20 19:20:44 +01:00
}
2012-09-10 01:10:46 +02:00
MmsSmsDatabase.Reader reader = null;
2012-09-10 01:10:46 +02:00
2011-12-20 19:20:44 +01:00
try {
reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId));
MessageRecord record = null;
if (reader != null) {
record = reader.getNext();
while (record != null && record.isDeleted()) {
record = reader.getNext();
}
}
if (record != null && !record.isDeleted()) {
updateThread(threadId, count, getFormattedBodyFor(record), getAttachmentUriFor(record),
2017-09-16 07:38:53 +02:00
record.getTimestamp(), record.getDeliveryStatus(), record.getDeliveryReceiptCount(),
record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount());
notifyConversationListListeners();
return false;
2012-10-01 04:56:29 +02:00
} else {
if (shouldDeleteEmptyThread) {
deleteThread(threadId);
notifyConversationListListeners();
return true;
}
return false;
2012-10-01 04:56:29 +02:00
}
2011-12-20 19:20:44 +01:00
} finally {
if (reader != null)
reader.close();
2011-12-20 19:20:44 +01:00
}
}
public void setPinned(long threadId, boolean pinned) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(IS_PINNED, pinned ? 1 : 0);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE,
new String[] {String.valueOf(threadId)});
notifyConversationListeners(threadId);
}
public void markAllAsRead(long threadId, boolean isGroupRecipient) {
List<MarkedMessageInfo> messages = setRead(threadId, true);
if (isGroupRecipient) {
for (MarkedMessageInfo message: messages) {
MarkReadReceiver.scheduleDeletion(context, message.getExpirationInfo());
}
} else {
MarkReadReceiver.process(context, messages);
}
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, false, 0);
}
private boolean deleteThreadOnEmpty(long threadId) {
Recipient threadRecipient = getRecipientForThreadId(threadId);
return threadRecipient != null && !threadRecipient.isOpenGroupRecipient();
}
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
if (messageRecord.isMms()) {
MmsMessageRecord record = (MmsMessageRecord) messageRecord;
if (record.getSharedContacts().size() > 0) {
Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0);
return ContactUtil.getStringSummary(context, contact).toString();
}
String attachmentString = record.getSlideDeck().getBody();
if (!attachmentString.isEmpty()) {
if (!messageRecord.getBody().isEmpty()) {
attachmentString = attachmentString + ": " + messageRecord.getBody();
}
return attachmentString;
}
}
return messageRecord.getBody();
}
private @Nullable Uri getAttachmentUriFor(MessageRecord record) {
2021-05-31 07:53:25 +02:00
if (!record.isMms() || record.isMmsNotification()) return null;
SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck();
Slide thumbnail = slideDeck.getThumbnailSlide();
if (thumbnail != null) {
return thumbnail.getThumbnailUri();
}
return null;
}
2017-11-17 00:21:46 +01:00
private @NonNull String createQuery(@NonNull String where, int limit) {
String projection = Util.join(COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION, ",");
2017-11-17 00:21:46 +01:00
String query =
"SELECT " + projection + " FROM " + TABLE_NAME +
" LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS +
" LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GROUP_ID +
" WHERE " + where +
" ORDER BY " + TABLE_NAME + "." + IS_PINNED + " DESC, " + TABLE_NAME + "." + DATE + " DESC";
2017-11-17 00:21:46 +01:00
if (limit > 0) {
query += " LIMIT " + limit;
}
return query;
}
Add emoji reacts support (#889) * feat: Add emoji reacts support * Remove message multi-selection * Add emoji reaction model * Add emoji reaction panel * Blur reacts panel background * Show emoji keyboard * Add emoji sprites * Update reaction proto * Emoji database updates * Emoji database refactor * Emoji reaction persistence * Optimize reactions retrieval * Fix emoji group query * Display emojis * Fix emoji persistence * Cleanup * Persistence refactor * Add reactions bottom sheet * Cleanup * Ui tweaks * React with any emoji * Show emoji react notifications * Remove reaction * Show reactions modal on long press * Click to react (+1) with an emoji * Click to react with an emoji * Enable emoji expand/collapse * fix: some compile issues from merge conflicts * fix: compile issues merging quote and media message UI * fix: xml IDs and adding in legacy is selected for future inclusion * Fix view constraints * Fix merge issue * Add message selection option in conversation context menu * Add sogs emoji integration * Handle sogs emoji reactions * Enable sending/deleting sogs emojis * fix: improve the visible message layout * fix: add file IDs to request parameters for message send (#940) * Fix open group polling from seqno instead of last hash (#939) * fix: reset seqno to get recent messages from open groups * build: upgrade build numbers * fix: actually run the migration * Using StringBuilder to construct request url * Fix reaction filter * fix: is_mms added in second projection query * Update default emojis * fix: include legacy and new open groups in server ID tracking (#941) * feat: add hidden moderator and admin roles, separated as they may be used independently in future (#942) * Cleanup * Fix view constraints * Add reactions capability check * Fix reactions alignment * Ui fixes * Display reactions list * feat: add formatted count strings * fix: account for negatives and add tests * Migrate old official open group locations for polling and adding (#932) * feat: adding in first part of open group migrations and tests for migration logic / helpers * feat: test code and migration logic for open groups in the case of no conflicts * feat: add in extra test cases and refactor code for migrator * refactor: migrate open group join URLs and references to server in adding new open groups to catch legacy and re-write it * refactor: joining open groups using OpenGroupUrlParser.kt now * fix: add in compile issues for renamed OpenGroupApi.kt from OpenGroupV2 * fix: prevent duplicates of http/https for new open group DNS and prevent adding new groups based on public key * fix: room and server swapped parameters * fix: replace default server for config messages * fix: actually using public key to de-dupe didn't work for rooms * build: bump version code and name * Display reactions list on open groups for moderators * Ui tweaks * Ui tweaks for moderation * Refactor * fix: compile issue * fix: de-duping joined queries in the get X from cursor * Restore import * fix: colouring the reaction overlay scrubber * fix: highlight colour, show reaction count if 1 or above * Cleanup * fix: light mode accent * fix: light / dark mode themeing in reactions dialog fragment * Emoji notification blinded id check * fix: show reaction list correctly and pass isUserModerator to bind methods * fix: remove unnecessary places for the moderator * fix: X button for removing own react not showing up properly * feat: add clear all header view * fix: migrate the clear all to the correct location * fix: use display instead of base * Truncate emoji sender ids * feat: add notify thread function in thread db * Notify threads on reaction received * fix: design fixes for the reaction list * fix: emoji reactions bottom sheet dialog UI designs * feat: add unsupported emoji reaction * fix: crash and doing vector properly * Fix reaction database queries * Fix background open group adder job * Show new open group reactions * Fetch a maximum of 5 reactors * Handle open group reactions polling conflicts * Add count to user reaction * Show number of additional reactors * fix: unreads set same as the unread query * fix: design changes * fix: update dependency to improve flexboxlayout behaviour, design consistencies * Add select message icon and update long press menu items order and wording * Fix crash on reactors dialog * fix: colours and backgrounds to match designs * fix: add header in recipient item * fix: margins * fix: alignments and layout issues for emoji reactions view * feat: add overflow previews and logic for overflow * Dim action bar * Add emoji search * Search index fix * Set count for 1:1 and closed group reactions when inserting in local database * Use on screen toolbar to allow overlaying * Show/hide scroll to bottom button * feat: add extended properties so it doesn't collapse on re-bind * Cleanup * feat: prevent keeping extended on rebinding if we get a new message ID * fix: long press works on devices now, fix release lint issue and crash for emoji search DBs from emoji builds * Display message timestamp * Fix modal items alignment * fix: sort order and emoji count in compareTo * Scale down really large messages to fit * Prevent closed group crash * Fix reaction author Co-authored-by: charles <charles@oxen.io> Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-09-04 13:03:32 +02:00
@NotNull
public List<ThreadRecord> getHttpOxenOpenGroups() {
String where = TABLE_NAME+"."+ADDRESS+" LIKE ?";
String selection = OpenGroupMigrator.HTTP_PREFIX+OpenGroupMigrator.OPEN_GET_SESSION_TRAILING_DOT_ENCODED +"%";
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(where, 0);
Cursor cursor = db.rawQuery(query, new String[]{selection});
if (cursor == null) {
return Collections.emptyList();
}
List<ThreadRecord> threads = new ArrayList<>();
try {
Reader reader = readerFor(cursor);
ThreadRecord record;
while ((record = reader.getNext()) != null) {
threads.add(record);
}
} finally {
cursor.close();
}
return threads;
}
@NotNull
public List<ThreadRecord> getLegacyOxenOpenGroups() {
String where = TABLE_NAME+"."+ADDRESS+" LIKE ?";
String selection = OpenGroupMigrator.LEGACY_GROUP_ENCODED_ID+"%";
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(where, 0);
Cursor cursor = db.rawQuery(query, new String[]{selection});
if (cursor == null) {
return Collections.emptyList();
}
List<ThreadRecord> threads = new ArrayList<>();
try {
Reader reader = readerFor(cursor);
ThreadRecord record;
while ((record = reader.getNext()) != null) {
threads.add(record);
}
} finally {
cursor.close();
}
return threads;
}
@NotNull
public List<ThreadRecord> getHttpsOxenOpenGroups() {
String where = TABLE_NAME+"."+ADDRESS+" LIKE ?";
String selection = OpenGroupMigrator.NEW_GROUP_ENCODED_ID+"%";
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(where, 0);
Cursor cursor = db.rawQuery(query, new String[]{selection});
if (cursor == null) {
return Collections.emptyList();
}
List<ThreadRecord> threads = new ArrayList<>();
try {
Reader reader = readerFor(cursor);
ThreadRecord record;
while ((record = reader.getNext()) != null) {
threads.add(record);
}
} finally {
cursor.close();
}
return threads;
}
public void migrateEncodedGroup(long threadId, @NotNull String newEncodedGroupId) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(ADDRESS, newEncodedGroupId);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""});
}
public void notifyThreadUpdated(long threadId) {
notifyConversationListeners(threadId);
}
public interface ProgressListener {
void onProgress(int complete, int total);
}
public Reader readerFor(Cursor cursor) {
return new Reader(cursor);
}
public class Reader implements Closeable {
private final Cursor cursor;
public Reader(Cursor cursor) {
this.cursor = cursor;
}
public ThreadRecord getNext() {
if (cursor == null || !cursor.moveToNext())
return null;
return getCurrent();
}
public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
2021-02-17 06:09:36 +01:00
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS)));
Optional<RecipientSettings> settings;
Optional<GroupRecord> groupRecord;
2017-11-13 18:11:58 +01:00
if (distributionType != DistributionTypes.ARCHIVE && distributionType != DistributionTypes.INBOX_ZERO) {
settings = DatabaseComponent.get(context).recipientDatabase().getRecipientSettings(cursor);
groupRecord = DatabaseComponent.get(context).groupDatabase().getGroup(cursor);
} else {
settings = Optional.absent();
groupRecord = Optional.absent();
}
Recipient recipient = Recipient.from(context, address, settings, groupRecord, true);
2018-02-02 03:29:09 +01:00
String body = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET));
2017-09-16 07:38:53 +02:00
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT));
int unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_COUNT));
2017-09-16 07:38:53 +02:00
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
Add a global search (#834) * feat: modifying search functionalities to include contacts * feat: add global search UI input layouts and color attributes * feat: add global search repository and model content * feat: adding diff callbacks and wiring up global search vm to views * feat: adding scroll to message, figuring out new query for recipient thread search * feat: messing with the search and highlighting functionality after wiring up bindings * fix: compile error from merge * fix: gradlew build errors * feat: filtering contacts by existing un-archived threads * refactor: prevent note to self breaking, update queries and logic in search repo to include member->group reverse searches * feat: adding home screen new redesigns for search * feat: replacing designs and adding new group subtitle text * feat: small design improvements and incrementing gradle build number to install on device * feat: add scrollbars for search * feat: replace isVisible for cancel button now that GlobalSearchInputLayout.kt replaces header * refactor: all queries are debounced not just all but 2 char * refactor: remove visibility modifiers for cancel icon * refactor: use simplified non-db and context related models in display, remove db get group members call from binding data * fix: use threadId instead of group's address * refactor: better close on cancel, removing only yourself from group member list in open groups * refactor: seed view back to inflated on create and visibility for empty placeholder and seed view text * refactor: fixing build issues and new designs for message list * refactor: use dynamic limit * refactor: include raw session ID string search for non-empty threads * fix: build lint errors * fix: build issues * feat: add in path to the settings activity * refactor: remove wildcard imports
2022-02-07 07:06:27 +01:00
boolean archived = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.ARCHIVED)) != 0;
2017-09-16 07:38:53 +02:00
int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS));
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.DELIVERY_RECEIPT_COUNT));
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.READ_RECEIPT_COUNT));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.EXPIRES_IN));
long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN));
Uri snippetUri = getSnippetUri(cursor);
Add a global search (#834) * feat: modifying search functionalities to include contacts * feat: add global search UI input layouts and color attributes * feat: add global search repository and model content * feat: adding diff callbacks and wiring up global search vm to views * feat: adding scroll to message, figuring out new query for recipient thread search * feat: messing with the search and highlighting functionality after wiring up bindings * fix: compile error from merge * fix: gradlew build errors * feat: filtering contacts by existing un-archived threads * refactor: prevent note to self breaking, update queries and logic in search repo to include member->group reverse searches * feat: adding home screen new redesigns for search * feat: replacing designs and adding new group subtitle text * feat: small design improvements and incrementing gradle build number to install on device * feat: add scrollbars for search * feat: replace isVisible for cancel button now that GlobalSearchInputLayout.kt replaces header * refactor: all queries are debounced not just all but 2 char * refactor: remove visibility modifiers for cancel icon * refactor: use simplified non-db and context related models in display, remove db get group members call from binding data * fix: use threadId instead of group's address * refactor: better close on cancel, removing only yourself from group member list in open groups * refactor: seed view back to inflated on create and visibility for empty placeholder and seed view text * refactor: fixing build issues and new designs for message list * refactor: use dynamic limit * refactor: include raw session ID string search for non-empty threads * fix: build lint errors * fix: build issues * feat: add in path to the settings activity * refactor: remove wildcard imports
2022-02-07 07:06:27 +01:00
boolean pinned = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.IS_PINNED)) != 0;
2017-09-16 07:38:53 +02:00
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
readReceiptCount = 0;
}
return new ThreadRecord(body, snippetUri, recipient, date, count,
unreadCount, threadId, deliveryReceiptCount, status, type,
distributionType, archived, expiresIn, lastSeen, readReceiptCount, pinned);
}
private @Nullable Uri getSnippetUri(Cursor cursor) {
if (cursor.isNull(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI))) {
return null;
}
try {
return Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI)));
} catch (IllegalArgumentException e) {
Log.w(TAG, e);
return null;
}
}
@Override
public void close() {
if (cursor != null) {
cursor.close();
}
}
}
2011-12-20 19:20:44 +01:00
}