Implement Story onboarding download job and message insertion.

This commit is contained in:
Alex Hart
2022-07-01 14:29:50 -03:00
committed by Greyson Parrelli
parent 2270dfaf21
commit 36ccf9ca54
17 changed files with 365 additions and 56 deletions

View File

@@ -201,7 +201,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract boolean hasSelfReplyInGroupStory(long parentStoryId);
public abstract @NonNull Cursor getStoryReplies(long parentStoryId);
public abstract @Nullable Long getOldestStorySendTimestamp();
public abstract int deleteStoriesOlderThan(long timestamp);
public abstract int deleteStoriesOlderThan(long timestamp, boolean hasSeenReleaseChannelStories);
public abstract @NonNull MessageDatabase.Reader getUnreadStories(@NonNull RecipientId recipientId, int limit);
public abstract @Nullable ParentStoryId.GroupReply getParentStoryIdForGroupReply(long messageId);
public abstract void deleteGroupStoryReplies(long parentStoryId);
@@ -728,9 +728,38 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
void onComplete();
}
public interface Reader extends Closeable {
/**
* Allows the developer to safely iterate over and close a cursor containing
* data for MessageRecord objects. Supports for-each loops as well as try-with-resources
* blocks.
*
* Readers are considered "one-shot" and it's on the caller to decide what needs
* to be done with the data. Once read, a reader cannot be read from again. This
* is by design, since reading data out of a cursor involves object creations and
* lookups, so it is in the best interest of app performance to only read out the
* data once. If you need to parse the list multiple times, it is recommended that
* you copy the iterable out into a normal List, or use extension methods such as
* partition.
*
* This reader does not support removal, since this would be considered a destructive
* database call.
*/
public interface Reader extends Closeable, Iterable<MessageRecord> {
/**
* @deprecated Use the Iterable interface instead.
*/
@Deprecated
MessageRecord getNext();
/**
* @deprecated Use the Iterable interface instead.
*/
@Deprecated
MessageRecord getCurrent();
/**
* From the {@link Closeable} interface, removing the IOException requirement.
*/
void close();
}

View File

@@ -98,9 +98,11 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -917,26 +919,28 @@ public class MmsDatabase extends MessageDatabase {
}
@Override
public int deleteStoriesOlderThan(long timestamp) {
public int deleteStoriesOlderThan(long timestamp, boolean hasSeenReleaseChannelStories) {
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
db.beginTransaction();
try {
String storiesBeforeTimestampWhere = IS_STORY_CLAUSE + " AND " + DATE_SENT + " < ?";
String[] sharedArgs = SqlUtil.buildArgs(timestamp);
String deleteStoryRepliesQuery = "DELETE FROM " + TABLE_NAME + " " +
"WHERE " + PARENT_STORY_ID + " > 0 AND " + PARENT_STORY_ID + " IN (" +
"SELECT " + ID + " " +
"FROM " + TABLE_NAME + " " +
"WHERE " + storiesBeforeTimestampWhere +
")";
String disassociateQuoteQuery = "UPDATE " + TABLE_NAME + " " +
"SET " + QUOTE_MISSING + " = 1, " + QUOTE_BODY + " = '' " +
"WHERE " + PARENT_STORY_ID + " < 0 AND ABS(" + PARENT_STORY_ID + ") IN (" +
"SELECT " + ID + " " +
"FROM " + TABLE_NAME + " " +
"WHERE " + storiesBeforeTimestampWhere +
")";
RecipientId releaseChannelRecipient = hasSeenReleaseChannelStories ? null : SignalStore.releaseChannelValues().getReleaseChannelRecipientId();
long releaseChannelThreadId = releaseChannelRecipient != null ? SignalDatabase.threads().getOrCreateThreadIdFor(Recipient.resolved(releaseChannelRecipient)) : -1;
String storiesBeforeTimestampWhere = IS_STORY_CLAUSE + " AND " + DATE_SENT + " < ? AND " + THREAD_ID + " != ?";
String[] sharedArgs = SqlUtil.buildArgs(timestamp, releaseChannelThreadId);
String deleteStoryRepliesQuery = "DELETE FROM " + TABLE_NAME + " " +
"WHERE " + PARENT_STORY_ID + " > 0 AND " + PARENT_STORY_ID + " IN (" +
"SELECT " + ID + " " +
"FROM " + TABLE_NAME + " " +
"WHERE " + storiesBeforeTimestampWhere +
")";
String disassociateQuoteQuery = "UPDATE " + TABLE_NAME + " " +
"SET " + QUOTE_MISSING + " = 1, " + QUOTE_BODY + " = '' " +
"WHERE " + PARENT_STORY_ID + " < 0 AND ABS(" + PARENT_STORY_ID + ") IN (" +
"SELECT " + ID + " " +
"FROM " + TABLE_NAME + " " +
"WHERE " + storiesBeforeTimestampWhere +
")";
db.execSQL(deleteStoryRepliesQuery, sharedArgs);
db.execSQL(disassociateQuoteQuery, sharedArgs);
@@ -2631,6 +2635,15 @@ public class MmsDatabase extends MessageDatabase {
}
}
/**
* MessageRecord reader which implements the Iterable interface. This allows it to
* be used with many Kotlin Extension Functions as well as with for-each loops.
*
* Note that it's the responsibility of the developer using the reader to ensure that:
*
* 1. They only utilize one of the two interfaces (legacy or iterator)
* 1. They close this reader after use, preferably via try-with-resources or a use block.
*/
public static class Reader implements MessageDatabase.Reader {
private final Cursor cursor;
@@ -2854,6 +2867,29 @@ public class MmsDatabase extends MessageDatabase {
cursor.close();
}
}
@NonNull
@Override
public Iterator<MessageRecord> iterator() {
return new ReaderIterator();
}
private class ReaderIterator implements Iterator<MessageRecord> {
@Override
public boolean hasNext() {
return cursor != null && cursor.getCount() != 0 && !cursor.isLast();
}
@Override
public MessageRecord next() {
MessageRecord record = getNext();
if (record == null) {
throw new NoSuchElementException();
}
return record;
}
}
}
private long generatePduCompatTimestamp(long time) {

View File

@@ -1477,7 +1477,7 @@ public class SmsDatabase extends MessageDatabase {
}
@Override
public int deleteStoriesOlderThan(long timestamp) {
public int deleteStoriesOlderThan(long timestamp, boolean hasSeenReleaseChannelStories) {
throw new UnsupportedOperationException();
}