From abb9acb4d8228cee6be245dcccfb12b4062b9d9a Mon Sep 17 00:00:00 2001 From: cmp Date: Fri, 10 Mar 2023 23:00:58 -0600 Subject: [PATCH] Export themes with hex colors by default. Read either int colors or hex colors on import. (#1289) --- .../CustomThemeListingActivity.java | 9 +- .../customtheme/CustomTheme.java | 92 ++++++++++++++++++- 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CustomThemeListingActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CustomThemeListingActivity.java index c2dc2afa..c47023e5 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CustomThemeListingActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CustomThemeListingActivity.java @@ -1,7 +1,5 @@ package ml.docilealligator.infinityforreddit.activities; -import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN; - import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -27,8 +25,7 @@ import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; +import com.google.gson.JsonParseException; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -272,9 +269,9 @@ public class CustomThemeListingActivity extends BaseActivity implements String json = item.coerceToText(this.getApplicationContext()).toString(); if (!TextUtils.isEmpty(json)) { try { - CustomTheme customTheme = new Gson().fromJson(json, CustomTheme.class); + CustomTheme customTheme = CustomTheme.fromJson(json); checkDuplicateAndImportTheme(customTheme, true); - } catch (JsonSyntaxException e) { + } catch (JsonParseException e) { Snackbar.make(coordinatorLayout, R.string.parse_theme_failed, Snackbar.LENGTH_SHORT).show(); } } else { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/customtheme/CustomTheme.java b/app/src/main/java/ml/docilealligator/infinityforreddit/customtheme/CustomTheme.java index 54c33e93..7acff53b 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/customtheme/CustomTheme.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/customtheme/CustomTheme.java @@ -1,13 +1,26 @@ package ml.docilealligator.infinityforreddit.customtheme; +import android.graphics.Color; + import androidx.annotation.NonNull; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.PrimaryKey; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Map; @Entity(tableName = "custom_themes") public class CustomTheme { @@ -197,10 +210,22 @@ public class CustomTheme { } public String getJSONModel() { - Gson gson = new Gson(); + Gson gson = getGsonBuilder().create(); return gson.toJson(this); } + private static GsonBuilder getGsonBuilder() { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(CustomTheme.class, new CustomThemeSerializer()); + builder.registerTypeAdapter(CustomTheme.class, new CustomThemeDeserializer()); + return builder; + } + + public static CustomTheme fromJson(String json) throws JsonParseException { + Gson gson = getGsonBuilder().create(); + return gson.fromJson(json, CustomTheme.class); + } + public static CustomTheme convertSettingsItemsToCustomTheme(ArrayList customThemeSettingsItems, String themeName) { CustomTheme customTheme = new CustomTheme(themeName); @@ -298,4 +323,69 @@ public class CustomTheme { return customTheme; } + + private static class CustomThemeSerializer implements JsonSerializer { + @Override + public JsonElement serialize(CustomTheme src, Type typeofSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + + for (Field field : src.getClass().getDeclaredFields()) { + try { + if (field.getType() == int.class) { + obj.addProperty(field.getName(), String.format("#%08X", field.getInt(src))); + } else { + obj.add(field.getName(), context.serialize(field.get(src))); + } + } catch (IllegalAccessException ignored) { + } + } + return obj; + } + } + + private static class CustomThemeDeserializer implements JsonDeserializer { + @Override + public CustomTheme deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + CustomTheme customTheme = new CustomTheme(); + + JsonObject obj = json.getAsJsonObject(); + + for (Map.Entry entry : obj.entrySet()) { + + Field field; + try { + field = customTheme.getClass().getDeclaredField(entry.getKey()); + } catch (NoSuchFieldException e) { + // Field not found, skip + continue; + } + + JsonElement value = entry.getValue(); + + try { + Class type = field.getType(); + if (int.class.equals(type)) { + if (value.getAsJsonPrimitive().isString()) { + // Hex or text color string + field.set(customTheme, Color.parseColor(value.getAsString())); + } else { + // Int color + field.set(customTheme, value.getAsInt()); + } + } else if (String.class.equals(type)) { + field.set(customTheme, value.getAsString()); + } else if (boolean.class.equals(type)) { + field.set(customTheme, value.getAsBoolean()); + } + + } catch (IllegalAccessException e) { + throw new JsonParseException("Failed to access theme field."); + } catch (IllegalArgumentException e) { + throw new JsonParseException("Invalid color string."); + } + + } + return customTheme; + } + } }