Update chat colors.

This commit is contained in:
Alex Hart
2021-05-03 11:34:41 -03:00
committed by Greyson Parrelli
parent 36fe150678
commit bcc5d485ab
164 changed files with 5817 additions and 1476 deletions

View File

@@ -0,0 +1,139 @@
package org.thoughtcrime.securesms.database
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.CursorUtil
import org.thoughtcrime.securesms.util.SqlUtil
class ChatColorsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Database(context, databaseHelper) {
companion object {
private const val TABLE_NAME = "chat_colors"
private const val ID = "_id"
private const val CHAT_COLORS = "chat_colors"
@JvmField
val CREATE_TABLE = """
CREATE TABLE $TABLE_NAME (
$ID INTEGER PRIMARY KEY AUTOINCREMENT,
$CHAT_COLORS BLOB
)
""".trimIndent()
}
fun getById(chatColorsId: ChatColors.Id): ChatColors {
val db = databaseHelper.readableDatabase
val projection = arrayOf(ID, CHAT_COLORS)
val args = SqlUtil.buildArgs(chatColorsId.longValue)
db.query(TABLE_NAME, projection, ID_WHERE, args, null, null, null)?.use {
if (it.moveToFirst()) {
return it.getChatColors()
}
}
throw IllegalArgumentException("Could not locate chat color $chatColorsId")
}
fun saveChatColors(chatColors: ChatColors): ChatColors {
return when (chatColors.id) {
is ChatColors.Id.Auto -> throw AssertionError("Saving 'auto' does not make sense")
is ChatColors.Id.BuiltIn -> chatColors
is ChatColors.Id.NotSet -> insertChatColors(chatColors)
is ChatColors.Id.Custom -> updateChatColors(chatColors)
}
}
fun getSavedChatColors(): List<ChatColors> {
val db = databaseHelper.readableDatabase
val projection = arrayOf(ID, CHAT_COLORS)
val result = mutableListOf<ChatColors>()
db.query(TABLE_NAME, projection, null, null, null, null, null)?.use {
while (it.moveToNext()) {
result.add(it.getChatColors())
}
}
return result
}
private fun insertChatColors(chatColors: ChatColors): ChatColors {
if (chatColors.id != ChatColors.Id.NotSet) {
throw IllegalArgumentException("Bad chat colors to insert.")
}
val db: SQLiteDatabase = databaseHelper.writableDatabase
val values = ContentValues(1).apply {
put(CHAT_COLORS, chatColors.serialize().toByteArray())
}
val rowId = db.insert(TABLE_NAME, null, values)
if (rowId == -1L) {
throw IllegalStateException("Failed to insert ChatColor into database")
}
notifyListeners()
return chatColors.withId(ChatColors.Id.forLongValue(rowId))
}
private fun updateChatColors(chatColors: ChatColors): ChatColors {
if (chatColors.id == ChatColors.Id.NotSet || chatColors.id == ChatColors.Id.BuiltIn) {
throw IllegalArgumentException("Bad chat colors to update.")
}
val db: SQLiteDatabase = databaseHelper.writableDatabase
val values = ContentValues(1).apply {
put(CHAT_COLORS, chatColors.serialize().toByteArray())
}
val rowsUpdated = db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(chatColors.id.longValue))
if (rowsUpdated < 1) {
throw IllegalStateException("Failed to update ChatColor in database")
}
if (SignalStore.chatColorsValues().chatColors?.id == chatColors.id) {
SignalStore.chatColorsValues().chatColors = chatColors
}
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
recipientDatabase.onUpdatedChatColors(chatColors)
notifyListeners()
return chatColors
}
fun deleteChatColors(chatColors: ChatColors) {
if (chatColors.id == ChatColors.Id.NotSet || chatColors.id == ChatColors.Id.BuiltIn) {
throw IllegalArgumentException("Cannot delete this chat color")
}
val db: SQLiteDatabase = databaseHelper.writableDatabase
db.delete(TABLE_NAME, ID_WHERE, SqlUtil.buildArgs(chatColors.id.longValue))
if (SignalStore.chatColorsValues().chatColors?.id == chatColors.id) {
SignalStore.chatColorsValues().chatColors = null
}
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
recipientDatabase.onDeletedChatColors(chatColors)
notifyListeners()
}
private fun notifyListeners() {
ApplicationDependencies.getDatabaseObserver().notifyChatColorsListeners()
}
private fun Cursor.getId(): Long = CursorUtil.requireLong(this, ID)
private fun Cursor.getChatColors(): ChatColors = ChatColors.forChatColor(
ChatColors.Id.forLongValue(getId()),
ChatColor.parseFrom(CursorUtil.requireBlob(this, CHAT_COLORS))
)
}

View File

@@ -64,6 +64,7 @@ public class DatabaseFactory {
private final RemappedRecordsDatabase remappedRecordsDatabase;
private final MentionDatabase mentionDatabase;
private final PaymentDatabase paymentDatabase;
private final ChatColorsDatabase chatColorsDatabase;
public static DatabaseFactory getInstance(Context context) {
if (instance == null) {
@@ -174,6 +175,10 @@ public class DatabaseFactory {
return getInstance(context).databaseHelper.getReadableDatabase().getSqlCipherDatabase();
}
public static ChatColorsDatabase getChatColorsDatabase(Context context) {
return getInstance(context).chatColorsDatabase;
}
public static void upgradeRestored(Context context, SQLiteDatabase database){
synchronized (lock) {
getInstance(context).databaseHelper.onUpgrade(database, database.getVersion(), -1);
@@ -223,6 +228,7 @@ public class DatabaseFactory {
this.remappedRecordsDatabase = new RemappedRecordsDatabase(context, databaseHelper);
this.mentionDatabase = new MentionDatabase(context, databaseHelper);
this.paymentDatabase = new PaymentDatabase(context, databaseHelper);
this.chatColorsDatabase = new ChatColorsDatabase(context, databaseHelper);
}
public void onApplicationLevelUpgrade(@NonNull Context context, @NonNull MasterSecret masterSecret,

View File

@@ -29,6 +29,7 @@ public final class DatabaseObserver {
private final Map<Long, Set<Observer>> verboseConversationObservers;
private final Map<UUID, Set<Observer>> paymentObservers;
private final Set<Observer> allPaymentsObservers;
private final Set<Observer> chatColorsObservers;
public DatabaseObserver(Application application) {
this.application = application;
@@ -38,6 +39,7 @@ public final class DatabaseObserver {
this.verboseConversationObservers = new HashMap<>();
this.paymentObservers = new HashMap<>();
this.allPaymentsObservers = new HashSet<>();
this.chatColorsObservers = new HashSet<>();
}
public void registerConversationListObserver(@NonNull Observer listener) {
@@ -70,12 +72,19 @@ public final class DatabaseObserver {
});
}
public void registerChatColorsObserver(@NonNull Observer listener) {
executor.execute(() -> {
chatColorsObservers.add(listener);
});
}
public void unregisterObserver(@NonNull Observer listener) {
executor.execute(() -> {
conversationListObservers.remove(listener);
unregisterMapped(conversationObservers, listener);
unregisterMapped(verboseConversationObservers, listener);
unregisterMapped(paymentObservers, listener);
chatColorsObservers.remove(listener);
});
}
@@ -131,6 +140,14 @@ public final class DatabaseObserver {
});
}
public void notifyChatColorsListeners() {
executor.execute(() -> {
for (Observer chatColorsObserver : chatColorsObservers) {
chatColorsObserver.onChanged();
}
});
}
private <K> void registerMapped(@NonNull Map<K, Set<Observer>> map, @NonNull K key, @NonNull Observer listener) {
Set<Observer> listeners = map.get(key);

View File

@@ -16,6 +16,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import net.sqlcipher.SQLException;
import net.sqlcipher.database.SQLiteConstraintException;
import org.jetbrains.annotations.NotNull;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.zkgroup.InvalidInputException;
@@ -23,12 +24,14 @@ import org.signal.zkgroup.groups.GroupMasterKey;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor;
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData;
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras;
@@ -62,7 +65,6 @@ import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.libsignal.util.guava.Preconditions;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
@@ -110,7 +112,7 @@ public class RecipientDatabase extends Database {
private static final String CALL_VIBRATE = "call_vibrate";
private static final String NOTIFICATION_CHANNEL = "notification_channel";
private static final String MUTE_UNTIL = "mute_until";
private static final String COLOR = "color";
//private static final String COLOR = "color";
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time";
@@ -145,6 +147,8 @@ public class RecipientDatabase extends Database {
public static final String ABOUT_EMOJI = "about_emoji";
private static final String EXTRAS = "extras";
private static final String GROUPS_IN_COMMON = "groups_in_common";
private static final String CHAT_COLORS = "chat_colors";
private static final String CUSTOM_CHAT_COLORS_ID = "custom_chat_colors_id";
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
private static final String SORT_NAME = "sort_name";
@@ -160,7 +164,7 @@ public class RecipientDatabase extends Database {
private static final String[] RECIPIENT_PROJECTION = new String[] {
ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE,
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
PROFILE_KEY, PROFILE_KEY_CREDENTIAL,
SYSTEM_JOINED_NAME, SYSTEM_GIVEN_NAME, SYSTEM_FAMILY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH,
@@ -172,7 +176,8 @@ public class RecipientDatabase extends Database {
MENTION_SETTING, WALLPAPER, WALLPAPER_URI,
MENTION_SETTING,
ABOUT, ABOUT_EMOJI,
EXTRAS, GROUPS_IN_COMMON
EXTRAS, GROUPS_IN_COMMON,
CHAT_COLORS, CUSTOM_CHAT_COLORS_ID
};
private static final String[] ID_PROJECTION = new String[]{ID};
@@ -321,7 +326,6 @@ public class RecipientDatabase extends Database {
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " +
MUTE_UNTIL + " INTEGER DEFAULT 0, " +
COLOR + " TEXT DEFAULT NULL, " +
SEEN_INVITE_REMINDER + " INTEGER DEFAULT " + InsightsBannerTier.NO_TIER.getId() + ", " +
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " +
@@ -354,8 +358,10 @@ public class RecipientDatabase extends Database {
WALLPAPER_URI + " TEXT DEFAULT NULL, " +
ABOUT + " TEXT DEFAULT NULL, " +
ABOUT_EMOJI + " TEXT DEFAULT NULL, " +
EXTRAS + " BLOB DEFAULT NULL, " +
GROUPS_IN_COMMON + " INTEGER DEFAULT 0);";
EXTRAS + " BLOB DEFAULT NULL, " +
GROUPS_IN_COMMON + " INTEGER DEFAULT 0, " +
CHAT_COLORS + " BLOB DEFAULT NULL, " +
CUSTOM_CHAT_COLORS_ID + " INTEGER DEFAULT 0);";
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
" FROM " + TABLE_NAME +
@@ -1025,10 +1031,6 @@ public class RecipientDatabase extends Database {
values.put(MUTE_UNTIL, contact.getMuteUntil());
values.put(STORAGE_SERVICE_ID, Base64.encodeBytes(contact.getId().getRaw()));
if (contact.isProfileSharingEnabled() && isInsert && !profileName.isEmpty()) {
values.put(COLOR, ContactColors.generateFor(profileName.toString()).serialize());
}
if (contact.hasUnknownFields()) {
values.put(STORAGE_PROTO, Base64.encodeBytes(contact.serializeUnknownFields()));
} else {
@@ -1170,7 +1172,6 @@ public class RecipientDatabase extends Database {
int messageVibrateState = CursorUtil.requireInt(cursor, MESSAGE_VIBRATE);
int callVibrateState = CursorUtil.requireInt(cursor, CALL_VIBRATE);
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
String serializedColor = CursorUtil.requireString(cursor, COLOR);
int insightsBannerTier = CursorUtil.requireInt(cursor, SEEN_INVITE_REMINDER);
int defaultSubscriptionId = CursorUtil.requireInt(cursor, DEFAULT_SUBSCRIPTION_ID);
int expireMessages = CursorUtil.requireInt(cursor, MESSAGE_EXPIRATION_TIME);
@@ -1195,21 +1196,15 @@ public class RecipientDatabase extends Database {
String storageKeyRaw = CursorUtil.requireString(cursor, STORAGE_SERVICE_ID);
int mentionSettingId = CursorUtil.requireInt(cursor, MENTION_SETTING);
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
byte[] serializedChatColors = CursorUtil.requireBlob(cursor, CHAT_COLORS);
long customChatColorsId = CursorUtil.requireLong(cursor, CUSTOM_CHAT_COLORS_ID);
String about = CursorUtil.requireString(cursor, ABOUT);
String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI);
boolean hasGroupsInCommon = CursorUtil.requireBoolean(cursor, GROUPS_IN_COMMON);
MaterialColor color;
byte[] profileKey = null;
ProfileKeyCredential profileKeyCredential = null;
try {
color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor);
} catch (MaterialColor.UnknownColorException e) {
Log.w(TAG, e);
color = null;
}
if (profileKeyString != null) {
try {
profileKey = Base64.decode(profileKeyString);
@@ -1247,6 +1242,15 @@ public class RecipientDatabase extends Database {
}
}
ChatColors chatColors = null;
if (serializedChatColors != null) {
try {
chatColors = ChatColors.forChatColor(ChatColors.Id.forLongValue(customChatColorsId), ChatColor.parseFrom(serializedChatColors));
} catch (InvalidProtocolBufferException e) {
Log.w(TAG, "Failed to parse chat colors.", e);
}
}
return new RecipientSettings(RecipientId.from(id),
uuid,
username,
@@ -1260,7 +1264,6 @@ public class RecipientDatabase extends Database {
VibrateState.fromId(callVibrateState),
Util.uri(messageRingtone),
Util.uri(callRingtone),
color,
defaultSubscriptionId,
expireMessages,
RegisteredState.fromId(registeredState),
@@ -1284,6 +1287,7 @@ public class RecipientDatabase extends Database {
storageKey,
MentionSetting.fromId(mentionSettingId),
chatWallpaper,
chatColors,
about,
aboutEmoji,
getSyncExtras(cursor),
@@ -1333,31 +1337,123 @@ public class RecipientDatabase extends Database {
return new BulkOperationsHandle(database);
}
public void setColor(@NonNull RecipientId id, @NonNull MaterialColor color) {
void onUpdatedChatColors(@NonNull ChatColors chatColors) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String where = CUSTOM_CHAT_COLORS_ID + " = ?";
String[] args = SqlUtil.buildArgs(chatColors.getId().getLongValue());
List<RecipientId> updated = new LinkedList<>();
try (Cursor cursor = database.query(TABLE_NAME, SqlUtil.buildArgs(ID), where, args, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
updated.add(RecipientId.from(CursorUtil.requireLong(cursor, ID)));
}
}
if (updated.isEmpty()) {
Log.d(TAG, "No recipients utilizing updated chat color.");
} else {
ContentValues values = new ContentValues(2);
values.put(CHAT_COLORS, chatColors.serialize().toByteArray());
values.put(CUSTOM_CHAT_COLORS_ID, chatColors.getId().getLongValue());
database.update(TABLE_NAME, values, where, args);
for (RecipientId recipientId : updated) {
Recipient.live(recipientId).refresh();
}
}
}
void onDeletedChatColors(@NonNull ChatColors chatColors) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String where = CUSTOM_CHAT_COLORS_ID + " = ?";
String[] args = SqlUtil.buildArgs(chatColors.getId().getLongValue());
List<RecipientId> updated = new LinkedList<>();
try (Cursor cursor = database.query(TABLE_NAME, SqlUtil.buildArgs(ID), where, args, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
updated.add(RecipientId.from(CursorUtil.requireLong(cursor, ID)));
}
}
if (updated.isEmpty()) {
Log.d(TAG, "No recipients utilizing deleted chat color.");
} else {
ContentValues values = new ContentValues(2);
values.put(CHAT_COLORS, (byte[]) null);
values.put(CUSTOM_CHAT_COLORS_ID, ChatColors.Id.NotSet.INSTANCE.getLongValue());
database.update(TABLE_NAME, values, where, args);
for (RecipientId recipientId : updated) {
Recipient.live(recipientId).refresh();
}
}
}
public int getColorUsageCount(@NotNull ChatColors chatColors) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] projection = SqlUtil.buildArgs("COUNT(*)");
String where = CUSTOM_CHAT_COLORS_ID + " = ?";
String[] args = SqlUtil.buildArgs(chatColors.getId().getLongValue());
try (Cursor cursor = db.query(TABLE_NAME, projection, where, args, null, null, null)) {
if (cursor == null) {
return 0;
} else {
cursor.moveToFirst();
return cursor.getInt(0);
}
}
}
public void clearAllColors() {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String where = CUSTOM_CHAT_COLORS_ID + " != ?";
String[] args = SqlUtil.buildArgs(ChatColors.Id.NotSet.INSTANCE.getLongValue());
List<RecipientId> toUpdate = new LinkedList<>();
try (Cursor cursor = database.query(TABLE_NAME, SqlUtil.buildArgs(ID), where, args, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
toUpdate.add(RecipientId.from(CursorUtil.requireLong(cursor, ID)));
}
}
if (toUpdate.isEmpty()) {
return;
}
ContentValues values = new ContentValues();
values.put(COLOR, color.serialize());
values.put(CHAT_COLORS, (byte[]) null);
values.put(CUSTOM_CHAT_COLORS_ID, ChatColors.Id.NotSet.INSTANCE.getLongValue());
database.update(TABLE_NAME, values, where, args);
for (RecipientId id : toUpdate) {
Recipient.live(id).refresh();
}
}
public void clearColor(@NonNull RecipientId id) {
ContentValues values = new ContentValues();
values.put(CHAT_COLORS, (byte[]) null);
values.put(CUSTOM_CHAT_COLORS_ID, ChatColors.Id.NotSet.INSTANCE.getLongValue());
if (update(id, values)) {
Recipient.live(id).refresh();
}
}
public void setColorIfNotSet(@NonNull RecipientId id, @NonNull MaterialColor color) {
if (setColorIfNotSetInternal(id, color)) {
public void setColor(@NonNull RecipientId id, @NonNull ChatColors color) {
ContentValues values = new ContentValues();
values.put(CHAT_COLORS, color.serialize().toByteArray());
values.put(CUSTOM_CHAT_COLORS_ID, color.getId().getLongValue());
if (update(id, values)) {
Recipient.live(id).refresh();
}
}
private boolean setColorIfNotSetInternal(@NonNull RecipientId id, @NonNull MaterialColor color) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String query = ID + " = ? AND " + COLOR + " IS NULL";
String[] args = new String[]{ id.serialize() };
ContentValues values = new ContentValues();
values.put(COLOR, color.serialize());
return db.update(TABLE_NAME, values, query, args) > 0;
}
public void setDefaultSubscriptionId(@NonNull RecipientId id, int defaultSubscriptionId) {
ContentValues values = new ContentValues();
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
@@ -1739,7 +1835,6 @@ public class RecipientDatabase extends Database {
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
boolean profiledUpdated = update(id, contentValues);
boolean colorUpdated = enabled && setColorIfNotSetInternal(id, ContactColors.generateFor(Recipient.resolved(id).getDisplayName(context)));
if (profiledUpdated && enabled) {
Optional<GroupDatabase.GroupRecord> group = DatabaseFactory.getGroupDatabase(context).getGroup(id);
@@ -1749,7 +1844,7 @@ public class RecipientDatabase extends Database {
}
}
if (profiledUpdated || colorUpdated) {
if (profiledUpdated) {
rotateStorageId(id);
Recipient.live(id).refresh();
StorageSyncHelper.scheduleSyncForDataChange();
@@ -2161,22 +2256,55 @@ public class RecipientDatabase extends Database {
return results;
}
public void updateSystemContactColors(@NonNull ColorUpdater updater) {
/**
* We no longer automatically generate a chat color. This method is used only
* in the case of a legacy migration and otherwise should not be called.
*/
@Deprecated
public void updateSystemContactColors() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Map<RecipientId, MaterialColor> updates = new HashMap<>();
Map<RecipientId, ChatColors> updates = new HashMap<>();
db.beginTransaction();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, COLOR, SYSTEM_JOINED_NAME}, SYSTEM_JOINED_NAME + " IS NOT NULL AND " + SYSTEM_JOINED_NAME + " != \"\"", null, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, "color", CHAT_COLORS, CUSTOM_CHAT_COLORS_ID, SYSTEM_JOINED_NAME}, SYSTEM_JOINED_NAME + " IS NOT NULL AND " + SYSTEM_JOINED_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_JOINED_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
long id = CursorUtil.requireLong(cursor, ID);
String serializedColor = CursorUtil.requireString(cursor, "color");
long customChatColorsId = CursorUtil.requireLong(cursor, CUSTOM_CHAT_COLORS_ID);
byte[] serializedChatColors = CursorUtil.requireBlob(cursor, CHAT_COLORS);
ChatColors chatColors;
if (serializedChatColors != null) {
try {
chatColors = ChatColors.forChatColor(ChatColors.Id.forLongValue(customChatColorsId), ChatColor.parseFrom(serializedChatColors));
} catch (InvalidProtocolBufferException e) {
chatColors = null;
}
} else {
chatColors = null;
}
if (chatColors != null) {
return;
}
if (serializedColor != null) {
try {
chatColors = ChatColorsMapper.getChatColors(MaterialColor.fromSerialized(serializedColor));
} catch (MaterialColor.UnknownColorException e) {
return;
}
} else {
return;
}
ContentValues contentValues = new ContentValues(1);
contentValues.put(COLOR, newColor.serialize());
contentValues.put(CHAT_COLORS, chatColors.serialize().toByteArray());
contentValues.put(CUSTOM_CHAT_COLORS_ID, chatColors.getId().getLongValue());
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] { String.valueOf(id) });
updates.put(RecipientId.from(id), newColor);
updates.put(RecipientId.from(id), chatColors);
}
} finally {
db.setTransactionSuccessful();
@@ -2754,7 +2882,8 @@ public class RecipientDatabase extends Database {
uuidValues.put(CALL_VIBRATE, uuidSettings.getCallVibrateState() != VibrateState.DEFAULT ? uuidSettings.getCallVibrateState().getId() : e164Settings.getCallVibrateState().getId());
uuidValues.put(NOTIFICATION_CHANNEL, uuidSettings.getNotificationChannel() != null ? uuidSettings.getNotificationChannel() : e164Settings.getNotificationChannel());
uuidValues.put(MUTE_UNTIL, uuidSettings.getMuteUntil() > 0 ? uuidSettings.getMuteUntil() : e164Settings.getMuteUntil());
uuidValues.put(COLOR, Optional.fromNullable(uuidSettings.getColor()).or(Optional.fromNullable(e164Settings.getColor())).transform(MaterialColor::serialize).orNull());
uuidValues.put(CHAT_COLORS, Optional.fromNullable(uuidSettings.getChatColors()).or(Optional.fromNullable(e164Settings.getChatColors())).transform(colors -> colors.serialize().toByteArray()).orNull());
uuidValues.put(CUSTOM_CHAT_COLORS_ID, Optional.fromNullable(uuidSettings.getChatColors()).or(Optional.fromNullable(e164Settings.getChatColors())).transform(colors -> colors.getId().getLongValue()).orNull());
uuidValues.put(SEEN_INVITE_REMINDER, e164Settings.getInsightsBannerTier().getId());
uuidValues.put(DEFAULT_SUBSCRIPTION_ID, e164Settings.getDefaultSubscriptionId().or(-1));
uuidValues.put(MESSAGE_EXPIRATION_TIME, uuidSettings.getExpireMessages() > 0 ? uuidSettings.getExpireMessages() : e164Settings.getExpireMessages());
@@ -2891,9 +3020,8 @@ public class RecipientDatabase extends Database {
refreshQualifyingValues.put(SYSTEM_CONTACT_URI, systemContactUri);
boolean updatedValues = update(id, refreshQualifyingValues);
boolean updatedColor = !TextUtils.isEmpty(joinedName) && setColorIfNotSetInternal(id, ContactColors.generateFor(joinedName));
if (updatedValues || updatedColor) {
if (updatedValues) {
pendingContactInfoMap.put(id, new PendingContactInfo(systemProfileName, photoUri, systemPhoneLabel, systemContactUri));
}
@@ -2951,7 +3079,7 @@ public class RecipientDatabase extends Database {
}
public interface ColorUpdater {
MaterialColor update(@NonNull String name, @Nullable String color);
ChatColors update(@NonNull String name, @Nullable MaterialColor materialColor);
}
public static class RecipientSettings {
@@ -2968,7 +3096,6 @@ public class RecipientDatabase extends Database {
private final VibrateState callVibrateState;
private final Uri messageRingtone;
private final Uri callRingtone;
private final MaterialColor color;
private final int defaultSubscriptionId;
private final int expireMessages;
private final RegisteredState registered;
@@ -2994,6 +3121,7 @@ public class RecipientDatabase extends Database {
private final byte[] storageId;
private final MentionSetting mentionSetting;
private final ChatWallpaper wallpaper;
private final ChatColors chatColors;
private final String about;
private final String aboutEmoji;
private final SyncExtras syncExtras;
@@ -3013,7 +3141,6 @@ public class RecipientDatabase extends Database {
@NonNull VibrateState callVibrateState,
@Nullable Uri messageRingtone,
@Nullable Uri callRingtone,
@Nullable MaterialColor color,
int defaultSubscriptionId,
int expireMessages,
@NonNull RegisteredState registered,
@@ -3037,6 +3164,7 @@ public class RecipientDatabase extends Database {
@Nullable byte[] storageId,
@NonNull MentionSetting mentionSetting,
@Nullable ChatWallpaper wallpaper,
@Nullable ChatColors chatColors,
@Nullable String about,
@Nullable String aboutEmoji,
@NonNull SyncExtras syncExtras,
@@ -3056,7 +3184,6 @@ public class RecipientDatabase extends Database {
this.callVibrateState = callVibrateState;
this.messageRingtone = messageRingtone;
this.callRingtone = callRingtone;
this.color = color;
this.defaultSubscriptionId = defaultSubscriptionId;
this.expireMessages = expireMessages;
this.registered = registered;
@@ -3082,6 +3209,7 @@ public class RecipientDatabase extends Database {
this.storageId = storageId;
this.mentionSetting = mentionSetting;
this.wallpaper = wallpaper;
this.chatColors = chatColors;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.syncExtras = syncExtras;
@@ -3117,10 +3245,6 @@ public class RecipientDatabase extends Database {
return groupType;
}
public @Nullable MaterialColor getColor() {
return color;
}
public boolean isBlocked() {
return blocked;
}
@@ -3241,6 +3365,10 @@ public class RecipientDatabase extends Database {
return wallpaper;
}
public @Nullable ChatColors getChatColors() {
return chatColors;
}
public @Nullable String getAbout() {
return about;
}

View File

@@ -22,10 +22,14 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColorsLegacy;
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.ChatColorsDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
@@ -77,6 +81,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatabase {
@@ -182,8 +187,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
private static final int STORAGE_SERVICE_REFACTOR = 97;
private static final int CLEAR_MMS_STORAGE_IDS = 98;
private static final int SERVER_GUID = 99;
private static final int CHAT_COLORS = 100;
private static final int DATABASE_VERSION = 99;
private static final int DATABASE_VERSION = 100;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -215,6 +221,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
db.execSQL(UnknownStorageIdDatabase.CREATE_TABLE);
db.execSQL(MentionDatabase.CREATE_TABLE);
db.execSQL(PaymentDatabase.CREATE_TABLE);
db.execSQL(ChatColorsDatabase.CREATE_TABLE);
executeStatements(db, SearchDatabase.CREATE_TABLE);
executeStatements(db, RemappedRecordsDatabase.CREATE_TABLE);
@@ -1463,6 +1470,27 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
db.execSQL("ALTER TABLE mms ADD COLUMN server_guid TEXT DEFAULT NULL");
}
if (oldVersion < CHAT_COLORS) {
db.execSQL("ALTER TABLE recipient ADD COLUMN chat_colors BLOB DEFAULT NULL");
db.execSQL("ALTER TABLE recipient ADD COLUMN custom_chat_colors_id INTEGER DEFAULT 0");
db.execSQL("CREATE TABLE chat_colors (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"chat_colors BLOB)");
Set<Map.Entry<MaterialColor, ChatColors>> entrySet = ChatColorsMapper.getEntrySet();
String where = "color = ? AND group_id is NULL";
for (Map.Entry<MaterialColor, ChatColors> entry : entrySet) {
String[] whereArgs = SqlUtil.buildArgs(entry.getKey().serialize());
ContentValues values = new ContentValues(2);
values.put("chat_colors", entry.getValue().serialize().toByteArray());
values.put("custom_chat_colors_id", entry.getValue().getId().getLongValue());
db.update("recipient", values, where, whereArgs);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();