Add ability to pin up to 4 conversations.

This commit is contained in:
Alex Hart
2020-08-13 09:31:55 -03:00
committed by Greyson Parrelli
parent 9892c4392e
commit d63e5165eb
18 changed files with 528 additions and 71 deletions

View File

@@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
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.MessagingDatabase.MarkedMessageInfo;
@@ -96,6 +97,7 @@ public class ThreadDatabase extends Database {
public static final String LAST_SEEN = "last_seen";
public static final String HAS_SENT = "has_sent";
private static final String LAST_SCROLLED = "last_scrolled";
private static final String PINNED = "pinned";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
DATE + " INTEGER DEFAULT 0, " +
@@ -118,16 +120,18 @@ public class ThreadDatabase extends Database {
HAS_SENT + " INTEGER DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
UNREAD_COUNT + " INTEGER DEFAULT 0, " +
LAST_SCROLLED + " INTEGER DEFAULT 0);";
LAST_SCROLLED + " INTEGER DEFAULT 0, " +
PINNED + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_ID + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
"CREATE INDEX IF NOT EXISTS thread_pinned_index ON " + TABLE_NAME + " (" + PINNED + ");",
};
private static final String[] THREAD_PROJECTION = {
ID, DATE, MESSAGE_COUNT, RECIPIENT_ID, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE,
SNIPPET_URI, SNIPPET_CONTENT_TYPE, SNIPPET_EXTRAS, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT, LAST_SCROLLED
SNIPPET_URI, SNIPPET_CONTENT_TYPE, SNIPPET_EXTRAS, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT, LAST_SCROLLED, PINNED
};
private static final List<String> TYPED_THREAD_PROJECTION = Stream.of(THREAD_PROJECTION)
@@ -579,8 +583,12 @@ public class ThreadDatabase extends Database {
return positions;
}
public Cursor getConversationList(long offset, long limit) {
return getConversationList("0", offset, limit);
public Cursor getPinnedConversationList(long offset, long limit) {
return getUnarchivedConversationList("1", offset, limit);
}
public Cursor getUnpinnedConversationList(long offset, long limit) {
return getUnarchivedConversationList("0", offset, limit);
}
public Cursor getArchivedConversationList(long offset, long limit) {
@@ -591,6 +599,16 @@ public class ThreadDatabase extends Database {
return getConversationList(archived, 0, 0);
}
private Cursor getUnarchivedConversationList(@NonNull String pinned, long offset, long limit) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(ARCHIVED + " = 0 AND " + MESSAGE_COUNT + " != 0 AND " + PINNED + " = ?", offset, limit);
Cursor cursor = db.rawQuery(query, new String[]{pinned});
setNotifyConversationListListeners(cursor);
return cursor;
}
private Cursor getConversationList(@NonNull String archived, long offset, long limit) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = createQuery(ARCHIVED + " = ? AND " + MESSAGE_COUNT + " != 0", offset, limit);
@@ -601,19 +619,19 @@ public class ThreadDatabase extends Database {
return cursor;
}
public int getUnarchivedConversationListCount() {
return getConversationListCount(false);
public int getPinnedConversationListCount() {
return getUnarchivedConversationListCount(true);
}
public int getUnpinnedConversationListCount() {
return getUnarchivedConversationListCount(false);
}
public int getArchivedConversationListCount() {
return getConversationListCount(true);
}
private int getConversationListCount(boolean archived) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] columns = new String[] { "COUNT(*)" };
String query = ARCHIVED + " = ? AND " + MESSAGE_COUNT + " != 0";
String[] args = new String[] { archived ? "1" : "0" };
String[] args = new String[] {"1"};
try (Cursor cursor = db.query(TABLE_NAME, columns, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
@@ -624,6 +642,46 @@ public class ThreadDatabase extends Database {
return 0;
}
private int getUnarchivedConversationListCount(boolean pinned) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] columns = new String[] { "COUNT(*)" };
String query = ARCHIVED + " = 0 AND " + MESSAGE_COUNT + " != 0 AND " + PINNED + " = ?";
String[] args = new String[] { pinned ? "1" : "0" };
try (Cursor cursor = db.query(TABLE_NAME, columns, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0);
}
}
return 0;
}
public void pinConversations(@NonNull Set<Long> threadIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
String placeholders = StringUtil.join(Stream.of(threadIds).map(unused -> "?").toList(), ",");
String selection = ID + " IN (" + placeholders + ")";
contentValues.put(PINNED, 1);
db.update(TABLE_NAME, contentValues, selection, SqlUtil.buildArgs(Stream.of(threadIds).toArray()));
notifyConversationListListeners();
}
public void unpinConversations(@NonNull Set<Long> threadIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
String placeholders = StringUtil.join(Stream.of(threadIds).map(unused -> "?").toList(), ",");
String selection = ID + " IN (" + placeholders + ")";
contentValues.put(PINNED, 0);
db.update(TABLE_NAME, contentValues, selection, SqlUtil.buildArgs(Stream.of(threadIds).toArray()));
notifyConversationListListeners();
}
public void archiveConversation(long threadId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
@@ -1122,6 +1180,7 @@ public class ThreadDatabase extends Database {
.setCount(cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT)))
.setUnreadCount(cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_COUNT)))
.setForcedUnread(cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.READ)) == ReadStatus.FORCED_UNREAD.serialize())
.setPinned(CursorUtil.requireBoolean(cursor, ThreadDatabase.PINNED))
.setExtra(extra)
.build();
}

View File

@@ -141,8 +141,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int BORDERLESS = 66;
private static final int REMAPPED_RECORDS = 67;
private static final int MENTIONS = 68;
private static final int PINNED_CONVERSATIONS = 69;
private static final int DATABASE_VERSION = 68;
private static final int DATABASE_VERSION = 69;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -991,6 +992,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE recipient ADD COLUMN mention_setting INTEGER DEFAULT 0");
}
if (oldVersion < PINNED_CONVERSATIONS) {
db.execSQL("ALTER TABLE thread ADD COLUMN pinned INTEGER DEFAULT 0");
db.execSQL("CREATE INDEX IF NOT EXISTS thread_pinned_index ON thread (pinned)");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@@ -55,6 +55,7 @@ public final class ThreadRecord {
private final boolean archived;
private final long expiresIn;
private final long lastSeen;
private final boolean isPinned;
private ThreadRecord(@NonNull Builder builder) {
this.threadId = builder.threadId;
@@ -75,6 +76,7 @@ public final class ThreadRecord {
this.archived = builder.archived;
this.expiresIn = builder.expiresIn;
this.lastSeen = builder.lastSeen;
this.isPinned = builder.isPinned;
}
public long getThreadId() {
@@ -187,6 +189,10 @@ public final class ThreadRecord {
else return true;
}
public boolean isPinned() {
return isPinned;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -205,6 +211,7 @@ public final class ThreadRecord {
archived == that.archived &&
expiresIn == that.expiresIn &&
lastSeen == that.lastSeen &&
isPinned == that.isPinned &&
body.equals(that.body) &&
recipient.equals(that.recipient) &&
Objects.equals(snippetUri, that.snippetUri) &&
@@ -231,7 +238,8 @@ public final class ThreadRecord {
distributionType,
archived,
expiresIn,
lastSeen);
lastSeen,
isPinned);
}
public static class Builder {
@@ -253,6 +261,7 @@ public final class ThreadRecord {
private boolean archived;
private long expiresIn;
private long lastSeen;
private boolean isPinned;
public Builder(long threadId) {
this.threadId = threadId;
@@ -348,6 +357,11 @@ public final class ThreadRecord {
return this;
}
public Builder setPinned(boolean isPinned) {
this.isPinned = isPinned;
return this;
}
public ThreadRecord build() {
if (distributionType == ThreadDatabase.DistributionTypes.CONVERSATION) {
Preconditions.checkArgument(threadId > 0);