mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Add trim conversations by time option.
This commit is contained in:
@@ -66,6 +66,7 @@ import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.thoughtcrime.securesms.util.MediaMetadataRetrieverUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
|
||||
import org.thoughtcrime.securesms.util.SetUtil;
|
||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
|
||||
@@ -83,10 +84,12 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -481,6 +484,39 @@ public class AttachmentDatabase extends Database {
|
||||
}
|
||||
}
|
||||
|
||||
public void trimAllAbandonedAttachments() {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
String selectAllMmsIds = "SELECT " + MmsDatabase.ID + " FROM " + MmsDatabase.TABLE_NAME;
|
||||
String selectDataInUse = "SELECT DISTINCT " + DATA + " FROM " + TABLE_NAME + " WHERE " + QUOTE + " = 0 AND " + MMS_ID + " IN (" + selectAllMmsIds + ")";
|
||||
String where = MMS_ID + " NOT IN (" + selectAllMmsIds + ") AND " + DATA + " NOT IN (" + selectDataInUse + ")";
|
||||
|
||||
db.delete(TABLE_NAME, where, null);
|
||||
}
|
||||
|
||||
public void deleteAbandonedAttachmentFiles() {
|
||||
Set<String> filesOnDisk = new HashSet<>();
|
||||
Set<String> filesInDb = new HashSet<>();
|
||||
|
||||
File attachmentDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||
for (File file : attachmentDirectory.listFiles()) {
|
||||
filesOnDisk.add(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().query(true, TABLE_NAME, new String[] { DATA, THUMBNAIL }, null, null, null, null, null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
filesInDb.add(CursorUtil.requireString(cursor, DATA));
|
||||
filesInDb.add(CursorUtil.requireString(cursor, THUMBNAIL));
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> onDiskButNotInDatabase = SetUtil.difference(filesOnDisk, filesInDb);
|
||||
|
||||
for (String filePath : onDiskButNotInDatabase) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
new File(filePath).delete();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
void deleteAllAttachments() {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
||||
@@ -115,6 +115,11 @@ public class GroupReceiptDatabase extends Database {
|
||||
db.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)});
|
||||
}
|
||||
|
||||
void deleteAbandonedRows() {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.delete(TABLE_NAME, MMS_ID + " NOT IN (SELECT " + MmsDatabase.ID + " FROM " + MmsDatabase.TABLE_NAME + ")", null);
|
||||
}
|
||||
|
||||
void deleteAllRows() {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.delete(TABLE_NAME, null, null);
|
||||
|
||||
@@ -21,9 +21,9 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList;
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||
import org.thoughtcrime.securesms.insights.InsightsConstants;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
@@ -143,6 +143,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||
abstract void deleteMessagesInThreadBeforeDate(long threadId, long date);
|
||||
abstract void deleteThreads(@NonNull Set<Long> threadIds);
|
||||
abstract void deleteAllThreads();
|
||||
abstract void deleteAbandonedMessages();
|
||||
|
||||
public abstract SQLiteDatabase beginTransaction();
|
||||
public abstract void endTransaction(SQLiteDatabase database);
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.database;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -41,7 +40,6 @@ import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.documents.Document;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
@@ -79,11 +77,9 @@ import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
@@ -1588,29 +1584,18 @@ public class MmsDatabase extends MessageDatabase {
|
||||
|
||||
@Override
|
||||
void deleteMessagesInThreadBeforeDate(long threadId, long date) {
|
||||
Cursor cursor = null;
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
String where = THREAD_ID + " = ? AND " + DATE_RECEIVED + " < " + date;
|
||||
|
||||
try {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
String where = THREAD_ID + " = ? AND (CASE (" + MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + ") ";
|
||||
db.delete(TABLE_NAME, where, SqlUtil.buildArgs(threadId));
|
||||
}
|
||||
|
||||
for (long outgoingType : Types.OUTGOING_MESSAGE_TYPES) {
|
||||
where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date;
|
||||
}
|
||||
@Override
|
||||
void deleteAbandonedMessages() {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
String where = THREAD_ID + " NOT IN (SELECT _id FROM " + ThreadDatabase.TABLE_NAME + ")";
|
||||
|
||||
where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)");
|
||||
|
||||
cursor = db.query(TABLE_NAME, new String[] {ID}, where, new String[] {threadId+""}, null, null, null);
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
Log.i(TAG, "Trimming: " + cursor.getLong(0));
|
||||
deleteMessage(cursor.getLong(0));
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
db.delete(TABLE_NAME, where, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -272,6 +272,18 @@ public class MmsSmsDatabase extends Database {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getMessageCountBeforeDate(long date) {
|
||||
String selection = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " < " + date;
|
||||
|
||||
try (Cursor cursor = queryTables(new String[] { "COUNT(*)" }, selection, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getSecureMessageCountForInsights() {
|
||||
int count = DatabaseFactory.getSmsDatabase(context).getSecureMessageCountForInsights();
|
||||
count += DatabaseFactory.getMmsDatabase(context).getSecureMessageCountForInsights();
|
||||
@@ -362,6 +374,29 @@ public class MmsSmsDatabase extends Database {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long getTimestampForFirstMessageAfterDate(long date) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
|
||||
String selection = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " > " + date;
|
||||
|
||||
try (Cursor cursor = queryTables(new String[] { MmsSmsColumns.NORMALIZED_DATE_RECEIVED }, selection, order, "1")) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getLong(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void deleteMessagesInThreadBeforeDate(long threadId, long trimBeforeDate) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, trimBeforeDate);
|
||||
DatabaseFactory.getMmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, trimBeforeDate);
|
||||
}
|
||||
|
||||
public void deleteAbandonedMessages() {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteAbandonedMessages();
|
||||
DatabaseFactory.getMmsDatabase(context).deleteAbandonedMessages();
|
||||
}
|
||||
|
||||
private Cursor queryTables(String[] projection, String selection, String order, String limit) {
|
||||
String[] mmsProjection = {MmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||
MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
||||
|
||||
@@ -968,16 +968,18 @@ public class SmsDatabase extends MessageDatabase {
|
||||
|
||||
@Override
|
||||
void deleteMessagesInThreadBeforeDate(long threadId, long date) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
String where = THREAD_ID + " = ? AND (CASE " + TYPE;
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
String where = THREAD_ID + " = ? AND " + DATE_RECEIVED + " < " + date;
|
||||
|
||||
for (long outgoingType : Types.OUTGOING_MESSAGE_TYPES) {
|
||||
where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date;
|
||||
}
|
||||
db.delete(TABLE_NAME, where, SqlUtil.buildArgs(threadId));
|
||||
}
|
||||
|
||||
where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)");
|
||||
@Override
|
||||
void deleteAbandonedMessages() {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
String where = THREAD_ID + " NOT IN (SELECT _id FROM " + ThreadDatabase.TABLE_NAME + ")";
|
||||
|
||||
db.delete(TABLE_NAME, where, new String[] {threadId + ""});
|
||||
db.delete(TABLE_NAME, where, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,8 +34,8 @@ import net.sqlcipher.database.SQLiteDatabase;
|
||||
import org.jsoup.helper.StringUtil;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
@@ -76,6 +76,9 @@ public class ThreadDatabase extends Database {
|
||||
|
||||
private static final String TAG = ThreadDatabase.class.getSimpleName();
|
||||
|
||||
public static final long NO_TRIM_BEFORE_DATE_SET = 0;
|
||||
public static final int NO_TRIM_MESSAGE_COUNT_SET = Integer.MAX_VALUE;
|
||||
|
||||
public static final String TABLE_NAME = "thread";
|
||||
public static final String ID = "_id";
|
||||
public static final String DATE = "date";
|
||||
@@ -258,53 +261,88 @@ public class ThreadDatabase extends Database {
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public void trimAllThreads(int length, ProgressListener listener) {
|
||||
Cursor cursor = null;
|
||||
int threadCount = 0;
|
||||
int complete = 0;
|
||||
public void trimAllThreads(int length, long trimBeforeDate) {
|
||||
if (length == NO_TRIM_MESSAGE_COUNT_SET && trimBeforeDate == NO_TRIM_BEFORE_DATE_SET) {
|
||||
return;
|
||||
}
|
||||
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
|
||||
GroupReceiptDatabase groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] { ID }, null, null, null, null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
trimThreadInternal(CursorUtil.requireLong(cursor, ID), length, trimBeforeDate);
|
||||
}
|
||||
}
|
||||
|
||||
db.beginTransaction();
|
||||
|
||||
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);
|
||||
}
|
||||
mmsSmsDatabase.deleteAbandonedMessages();
|
||||
attachmentDatabase.trimAllAbandonedAttachments();
|
||||
groupReceiptDatabase.deleteAbandonedRows();
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
attachmentDatabase.deleteAbandonedAttachmentFiles();
|
||||
|
||||
notifyAttachmentListeners();
|
||||
notifyStickerListeners();
|
||||
notifyStickerPackListeners();
|
||||
}
|
||||
|
||||
public void trimThread(long threadId, int length) {
|
||||
Log.i(TAG, "Trimming thread: " + threadId + " to: " + length);
|
||||
Cursor cursor = null;
|
||||
public void trimThread(long threadId, int length, long trimBeforeDate) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
|
||||
GroupReceiptDatabase groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
|
||||
db.beginTransaction();
|
||||
|
||||
try {
|
||||
cursor = DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId);
|
||||
|
||||
if (cursor != null && length > 0 && cursor.getCount() > length) {
|
||||
Log.w(TAG, "Cursor count is greater than length!");
|
||||
cursor.moveToPosition(length - 1);
|
||||
|
||||
long lastTweetDate = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.NORMALIZED_DATE_RECEIVED));
|
||||
|
||||
Log.i(TAG, "Cut off tweet date: " + lastTweetDate);
|
||||
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
|
||||
DatabaseFactory.getMmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
|
||||
|
||||
update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
trimThreadInternal(threadId, length, trimBeforeDate);
|
||||
mmsSmsDatabase.deleteAbandonedMessages();
|
||||
attachmentDatabase.trimAllAbandonedAttachments();
|
||||
groupReceiptDatabase.deleteAbandonedRows();
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
attachmentDatabase.deleteAbandonedAttachmentFiles();
|
||||
|
||||
notifyAttachmentListeners();
|
||||
notifyStickerListeners();
|
||||
notifyStickerPackListeners();
|
||||
}
|
||||
|
||||
private void trimThreadInternal(long threadId, int length, long trimBeforeDate) {
|
||||
if (length == NO_TRIM_MESSAGE_COUNT_SET && trimBeforeDate == NO_TRIM_BEFORE_DATE_SET) {
|
||||
return;
|
||||
}
|
||||
|
||||
long trimDate = trimBeforeDate;
|
||||
|
||||
if (length != NO_TRIM_MESSAGE_COUNT_SET) {
|
||||
try (Cursor cursor = DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId)) {
|
||||
if (cursor != null && length > 0 && cursor.getCount() > length) {
|
||||
cursor.moveToPosition(length - 1);
|
||||
trimDate = Math.max(trimDate, cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.NORMALIZED_DATE_RECEIVED)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trimDate != NO_TRIM_BEFORE_DATE_SET) {
|
||||
Log.i(TAG, "Trimming thread: " + threadId + " before: " + trimBeforeDate);
|
||||
|
||||
DatabaseFactory.getMmsSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, trimBeforeDate);
|
||||
|
||||
update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1153,10 +1191,6 @@ public class ThreadDatabase extends Database {
|
||||
return query;
|
||||
}
|
||||
|
||||
public interface ProgressListener {
|
||||
void onProgress(int complete, int total);
|
||||
}
|
||||
|
||||
public Reader readerFor(Cursor cursor) {
|
||||
return new Reader(cursor);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user