From 547b7a3c6fe8a1f8f4e5345a8b1d7068b86bb49f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 25 Sep 2018 15:41:42 -0700 Subject: [PATCH] Migrate legacy color palette. We don't store non-user-selected colors in the database. That means that when we update the palette, we still have to hash based off of the legacy palette when generating a color if we want to migrate to a similar-looking color. Unfortunately, because the new palette is smaller, some colors are "overloaded", meaning that when we hash based off of the legacy palette, some colors will be more/less common than others. To fix this, we simply persist all current colors in the database, then switch our hashing list to what we really want. --- .../securesms/DatabaseUpgradeActivity.java | 21 +++++++++ .../securesms/color/MaterialColor.java | 2 +- .../contacts/avatars/ContactColors.java | 43 ++++++++----------- .../contacts/avatars/ContactColorsLegacy.java | 40 +++++++++++++++++ .../securesms/database/RecipientDatabase.java | 37 +++++++++++++++- 5 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java diff --git a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java index 535b06cac2..219b249976 100644 --- a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java +++ b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java @@ -30,6 +30,8 @@ import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.persistence.JavaJobSerializer; import org.thoughtcrime.securesms.jobmanager.persistence.PersistentStorage; +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.contacts.avatars.ContactColorsLegacy; import org.thoughtcrime.securesms.logging.Log; import android.view.View; import android.widget.ProgressBar; @@ -50,6 +52,7 @@ import org.thoughtcrime.securesms.jobs.PushDecryptJob; import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.notifications.MessageNotifier; +import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -91,6 +94,7 @@ public class DatabaseUpgradeActivity extends BaseActivity { public static final int BAD_IMPORT_CLEANUP = 373; public static final int IMAGE_CACHE_CLEANUP = 406; public static final int WORKMANAGER_MIGRATION = 408; + public static final int COLOR_MIGRATION = 412; private static final SortedSet UPGRADE_VERSIONS = new TreeSet() {{ add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); @@ -115,6 +119,7 @@ public class DatabaseUpgradeActivity extends BaseActivity { add(BAD_IMPORT_CLEANUP); add(IMAGE_CACHE_CLEANUP); add(WORKMANAGER_MIGRATION); + add(COLOR_MIGRATION); }}; private MasterSecret masterSecret; @@ -337,6 +342,22 @@ public class DatabaseUpgradeActivity extends BaseActivity { } } + if (params[0] < COLOR_MIGRATION) { + long startTime = System.currentTimeMillis(); + DatabaseFactory.getRecipientDatabase(context).updateSystemContactColors((name, color) -> { + if (color != null) { + try { + return MaterialColor.fromSerialized(color); + } catch (MaterialColor.UnknownColorException e) { + Log.w(TAG, "Encountered an unknown color during legacy color migration.", e); + return ContactColorsLegacy.generateFor(name); + } + } + return ContactColorsLegacy.generateFor(name); + }); + Log.i(TAG, "Color migration took " + (System.currentTimeMillis() - startTime) + " ms"); + } + return null; } diff --git a/src/org/thoughtcrime/securesms/color/MaterialColor.java b/src/org/thoughtcrime/securesms/color/MaterialColor.java index c64d3b0221..3066302f84 100644 --- a/src/org/thoughtcrime/securesms/color/MaterialColor.java +++ b/src/org/thoughtcrime/securesms/color/MaterialColor.java @@ -29,7 +29,7 @@ public enum MaterialColor { private static final Map COLOR_MATCHES = new HashMap() {{ put("red", CRIMSON); - put("deep_orange", VERMILLION); + put("deep_orange", CRIMSON); put("orange", VERMILLION); put("amber", VERMILLION); put("brown", BURLAP); diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java b/src/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java index 2e91385120..12c1153234 100644 --- a/src/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java +++ b/src/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java @@ -5,35 +5,30 @@ import android.support.annotation.NonNull; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColors; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class ContactColors { public static final MaterialColor UNKNOWN_COLOR = MaterialColor.STEEL; - private static final String[] LEGACY_PALETTE = new String[] { - "red", - "pink", - "purple", - "deep_purple", - "indigo", - "blue", - "light_blue", - "cyan", - "teal", - "green", - "light_green", - "orange", - "deep_orange", - "amber", - "blue_grey" - }; + private static final List CONVERSATION_PALETTE = new ArrayList<>(Arrays.asList( + MaterialColor.PLUM, + MaterialColor.CRIMSON, + MaterialColor.VERMILLION, + MaterialColor.VIOLET, + MaterialColor.BLUE, + MaterialColor.INDIGO, + MaterialColor.FOREST, + MaterialColor.WINTERGREEN, + MaterialColor.TEAL, + MaterialColor.BURLAP, + MaterialColor.TAUPE, + MaterialColor.STEEL + )); public static MaterialColor generateFor(@NonNull String name) { - String serialized = LEGACY_PALETTE[Math.abs(name.hashCode()) % LEGACY_PALETTE.length]; - try { - return MaterialColor.fromSerialized(serialized); - } catch (MaterialColor.UnknownColorException e) { - return MaterialColors.CONVERSATION_PALETTE.get(Math.abs(name.hashCode()) % MaterialColors.CONVERSATION_PALETTE.size()); - } + return CONVERSATION_PALETTE.get(Math.abs(name.hashCode()) % CONVERSATION_PALETTE.size()); } - } diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java b/src/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java new file mode 100644 index 0000000000..ec99f4d36d --- /dev/null +++ b/src/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java @@ -0,0 +1,40 @@ +package org.thoughtcrime.securesms.contacts.avatars; + +import android.support.annotation.NonNull; + +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.color.MaterialColors; + +/** + * Used for migrating legacy colors to modern colors. For normal color generation, use + * {@link ContactColors}. + */ +public class ContactColorsLegacy { + + private static final String[] LEGACY_PALETTE = new String[] { + "red", + "pink", + "purple", + "deep_purple", + "indigo", + "blue", + "light_blue", + "cyan", + "teal", + "green", + "light_green", + "orange", + "deep_orange", + "amber", + "blue_grey" + }; + + public static MaterialColor generateFor(@NonNull String name) { + String serialized = LEGACY_PALETTE[Math.abs(name.hashCode()) % LEGACY_PALETTE.length]; + try { + return MaterialColor.fromSerialized(serialized); + } catch (MaterialColor.UnknownColorException e) { + return ContactColors.generateFor(name); + } + } +} diff --git a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java index 28f887b4ff..4cca0b7f42 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -27,6 +27,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; public class RecipientDatabase extends Database { @@ -150,7 +151,6 @@ public class RecipientDatabase extends Database { return new RecipientReader(context, cursor); } - public Optional getRecipientSettings(@NonNull Address address) { SQLiteDatabase database = databaseHelper.getReadableDatabase(); Cursor cursor = null; @@ -410,6 +410,35 @@ public class RecipientDatabase extends Database { return results; } + public void updateSystemContactColors(@NonNull ColorUpdater updater) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Map updates = new HashMap<>(); + + db.beginTransaction(); + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)), + cursor.getString(cursor.getColumnIndexOrThrow(COLOR))); + + ContentValues contentValues = new ContentValues(1); + contentValues.put(COLOR, newColor.serialize()); + db.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[]{address.serialize()}); + + updates.put(address, newColor); + } + } finally { + db.setTransactionSuccessful(); + db.endTransaction(); + + Stream.of(updates.entrySet()).forEach(entry -> { + Recipient.applyCached(entry.getKey(), recipient -> { + recipient.setColor(entry.getValue()); + }); + }); + } + } + // XXX This shouldn't be here, and is just a temporary workaround public RegisteredState isRegistered(@NonNull Address address) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); @@ -472,6 +501,10 @@ public class RecipientDatabase extends Database { } } + public interface ColorUpdater { + MaterialColor update(@NonNull String name, @Nullable String color); + } + public static class RecipientSettings { private final boolean blocked; private final long muteUntil; @@ -633,7 +666,7 @@ public class RecipientDatabase extends Database { } public @Nullable Recipient getNext() { - if (!cursor.moveToNext()) { + if (cursor != null && !cursor.moveToNext()) { return null; }