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.
master
Greyson Parrelli 2018-09-25 15:41:42 -07:00
parent 5eec3c9541
commit 547b7a3c6f
5 changed files with 116 additions and 27 deletions

View File

@ -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<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
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;
}

View File

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

View File

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

View File

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

View File

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