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.
pull/1/head
Greyson Parrelli 7 years ago
parent 5eec3c9541
commit 547b7a3c6f

@ -30,6 +30,8 @@ import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.persistence.JavaJobSerializer; import org.thoughtcrime.securesms.jobmanager.persistence.JavaJobSerializer;
import org.thoughtcrime.securesms.jobmanager.persistence.PersistentStorage; 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 org.thoughtcrime.securesms.logging.Log;
import android.view.View; import android.view.View;
import android.widget.ProgressBar; import android.widget.ProgressBar;
@ -50,6 +52,7 @@ import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences; 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 BAD_IMPORT_CLEANUP = 373;
public static final int IMAGE_CACHE_CLEANUP = 406; public static final int IMAGE_CACHE_CLEANUP = 406;
public static final int WORKMANAGER_MIGRATION = 408; public static final int WORKMANAGER_MIGRATION = 408;
public static final int COLOR_MIGRATION = 412;
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{ private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@ -115,6 +119,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(BAD_IMPORT_CLEANUP); add(BAD_IMPORT_CLEANUP);
add(IMAGE_CACHE_CLEANUP); add(IMAGE_CACHE_CLEANUP);
add(WORKMANAGER_MIGRATION); add(WORKMANAGER_MIGRATION);
add(COLOR_MIGRATION);
}}; }};
private MasterSecret masterSecret; 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; return null;
} }

@ -29,7 +29,7 @@ public enum MaterialColor {
private static final Map<String, MaterialColor> COLOR_MATCHES = new HashMap<String, MaterialColor>() {{ private static final Map<String, MaterialColor> COLOR_MATCHES = new HashMap<String, MaterialColor>() {{
put("red", CRIMSON); put("red", CRIMSON);
put("deep_orange", VERMILLION); put("deep_orange", CRIMSON);
put("orange", VERMILLION); put("orange", VERMILLION);
put("amber", VERMILLION); put("amber", VERMILLION);
put("brown", BURLAP); put("brown", BURLAP);

@ -5,35 +5,30 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.color.MaterialColors; import org.thoughtcrime.securesms.color.MaterialColors;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ContactColors { public class ContactColors {
public static final MaterialColor UNKNOWN_COLOR = MaterialColor.STEEL; public static final MaterialColor UNKNOWN_COLOR = MaterialColor.STEEL;
private static final String[] LEGACY_PALETTE = new String[] { private static final List<MaterialColor> CONVERSATION_PALETTE = new ArrayList<>(Arrays.asList(
"red", MaterialColor.PLUM,
"pink", MaterialColor.CRIMSON,
"purple", MaterialColor.VERMILLION,
"deep_purple", MaterialColor.VIOLET,
"indigo", MaterialColor.BLUE,
"blue", MaterialColor.INDIGO,
"light_blue", MaterialColor.FOREST,
"cyan", MaterialColor.WINTERGREEN,
"teal", MaterialColor.TEAL,
"green", MaterialColor.BURLAP,
"light_green", MaterialColor.TAUPE,
"orange", MaterialColor.STEEL
"deep_orange", ));
"amber",
"blue_grey"
};
public static MaterialColor generateFor(@NonNull String name) { public static MaterialColor generateFor(@NonNull String name) {
String serialized = LEGACY_PALETTE[Math.abs(name.hashCode()) % LEGACY_PALETTE.length]; return CONVERSATION_PALETTE.get(Math.abs(name.hashCode()) % CONVERSATION_PALETTE.size());
try {
return MaterialColor.fromSerialized(serialized);
} catch (MaterialColor.UnknownColorException e) {
return MaterialColors.CONVERSATION_PALETTE.get(Math.abs(name.hashCode()) % MaterialColors.CONVERSATION_PALETTE.size());
}
} }
} }

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

@ -27,6 +27,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
public class RecipientDatabase extends Database { public class RecipientDatabase extends Database {
@ -150,7 +151,6 @@ public class RecipientDatabase extends Database {
return new RecipientReader(context, cursor); return new RecipientReader(context, cursor);
} }
public Optional<RecipientSettings> getRecipientSettings(@NonNull Address address) { public Optional<RecipientSettings> getRecipientSettings(@NonNull Address address) {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
@ -410,6 +410,35 @@ public class RecipientDatabase extends Database {
return results; return results;
} }
public void updateSystemContactColors(@NonNull ColorUpdater updater) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Map<Address, MaterialColor> 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 // XXX This shouldn't be here, and is just a temporary workaround
public RegisteredState isRegistered(@NonNull Address address) { public RegisteredState isRegistered(@NonNull Address address) {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); 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 { public static class RecipientSettings {
private final boolean blocked; private final boolean blocked;
private final long muteUntil; private final long muteUntil;
@ -633,7 +666,7 @@ public class RecipientDatabase extends Database {
} }
public @Nullable Recipient getNext() { public @Nullable Recipient getNext() {
if (!cursor.moveToNext()) { if (cursor != null && !cursor.moveToNext()) {
return null; return null;
} }

Loading…
Cancel
Save