Add trim conversations by time option.

This commit is contained in:
Cody Henthorne
2020-09-03 17:52:44 -04:00
parent 6a14dc69c0
commit bcd27355f9
36 changed files with 1183 additions and 233 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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