From 7f026edbd0a18206c8f2be1a1db1f5b47401ee86 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 13 Jun 2022 16:38:10 +0200 Subject: [PATCH] Fossil Hybrid HR: Generate watchface preview image --- .../HybridHRWatchfaceDesignerActivity.java | 79 +------------ .../qhybrid/HybridHRWatchfaceFactory.java | 110 +++++++++++++++++- 2 files changed, 115 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java index 3464ca3a5..539fcd676 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java @@ -351,79 +351,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem for (int i = 0; i < layout.length(); i++) { JSONObject layoutItem = layout.getJSONObject(i); if (layoutItem.getString("type").equals("comp")) { - String widgetName = layoutItem.getString("name"); - String widgetTimezone = null; - int widgetUpdateTimeout = -1; - boolean widgetTimeoutHideText = true; - boolean widgetTimeoutShowCircle = true; - switch (widgetName) { - case "dateSSE": - widgetName = "widgetDate"; - break; - case "weatherSSE": - widgetName = "widgetWeather"; - break; - case "stepsSSE": - widgetName = "widgetSteps"; - break; - case "hrSSE": - widgetName = "widgetHR"; - break; - case "batterySSE": - widgetName = "widgetBattery"; - break; - case "caloriesSSE": - widgetName = "widgetCalories"; - break; - case "activeMinutesSSE": - widgetName = "widgetActiveMins"; - break; - case "chanceOfRainSSE": - widgetName = "widgetChanceOfRain"; - break; - case "timeZone2SSE": - widgetName = "widget2ndTZ"; - break; - } - int widgetColor = layoutItem.getString("color").equals("white") ? HybridHRWatchfaceWidget.COLOR_WHITE : HybridHRWatchfaceWidget.COLOR_BLACK; - if (widgetName.startsWith("widget2ndTZ")) { - try { - widgetName = "widget2ndTZ"; - JSONObject widgetData = layoutItem.getJSONObject("data"); - widgetTimezone = widgetData.getString("tzName"); - widgets.add(new HybridHRWatchfaceWidget(widgetName, - layoutItem.getJSONObject("pos").getInt("x"), - layoutItem.getJSONObject("pos").getInt("y"), - layoutItem.getJSONObject("size").getInt("w"), - layoutItem.getJSONObject("size").getInt("h"), - widgetColor, - widgetTimezone)); - } catch (JSONException e) { - LOG.error("Couldn't determine tzName!", e); - } - } else if (widgetName.startsWith("widgetCustom")) { - widgetName = "widgetCustom"; - JSONObject widgetData = layoutItem.getJSONObject("data"); - widgetUpdateTimeout = widgetData.getInt("update_timeout"); - widgetTimeoutHideText = widgetData.getBoolean("timeout_hide_text"); - widgetTimeoutShowCircle = widgetData.getBoolean("timeout_show_circle"); - widgets.add(new HybridHRWatchfaceWidget(widgetName, - layoutItem.getJSONObject("pos").getInt("x"), - layoutItem.getJSONObject("pos").getInt("y"), - layoutItem.getJSONObject("size").getInt("w"), - layoutItem.getJSONObject("size").getInt("h"), - widgetColor, - widgetUpdateTimeout, - widgetTimeoutHideText, - widgetTimeoutShowCircle)); - } else { - widgets.add(new HybridHRWatchfaceWidget(widgetName, - layoutItem.getJSONObject("pos").getInt("x"), - layoutItem.getJSONObject("pos").getInt("y"), - layoutItem.getJSONObject("size").getInt("w"), - layoutItem.getJSONObject("size").getInt("h"), - widgetColor)); - } + widgets.add(HybridHRWatchfaceFactory.parseWidgetJSON(layoutItem)); } } } catch (JSONException e) { @@ -838,6 +766,11 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem GBApplication.deviceService().onInstallApp(tempAppFileUri); FossilHRInstallHandler.saveAppInCache(fossilFile, processedBackgroundImage, mCoordinator, HybridHRWatchfaceDesignerActivity.this); } + Bitmap previewImage = wfFactory.getPreviewImage(this); + File previewFile = new File(cacheDir, app.getUUID().toString() + "_preview.png"); + FileOutputStream previewFOS = new FileOutputStream(previewFile); + previewImage.compress(Bitmap.CompressFormat.PNG, 9, previewFOS); + previewFOS.close(); } } catch (IOException e) { LOG.warn("Error while creating and uploading watchface", e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java index be473880a..6e54ae74e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java @@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Canvas; import org.json.JSONArray; import org.json.JSONException; @@ -33,12 +34,16 @@ import java.util.LinkedHashMap; import java.util.TimeZone; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.ImageConverter; +import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil; public class HybridHRWatchfaceFactory { - private final Logger LOG = LoggerFactory.getLogger(HybridHRWatchfaceFactory.class); + private static final Logger LOG = LoggerFactory.getLogger(HybridHRWatchfaceFactory.class); private String watchfaceName; private HybridHRWatchfaceSettings settings; private Bitmap background; + private Bitmap previewImage; + private static final int PREVIEW_WIDTH = 192; + private static final int PREVIEW_HEIGHT = 192; private ArrayList widgets = new ArrayList<>(); public HybridHRWatchfaceFactory(String name) { @@ -140,6 +145,8 @@ public class HybridHRWatchfaceFactory { public byte[] getWapp(Context context) throws IOException { byte[] backgroundBytes = ImageConverter.encodeToRawImage(ImageConverter.get2BitsRAWImageBytes(background)); InputStream backgroundStream = new ByteArrayInputStream(backgroundBytes); + byte[] previewBytes = ImageConverter.encodeToRLEImage(ImageConverter.get2BitsRLEImageBytes(Bitmap.createScaledBitmap(getPreviewImage(context), PREVIEW_WIDTH, PREVIEW_HEIGHT, true)), PREVIEW_HEIGHT, PREVIEW_WIDTH); + InputStream previewStream = new ByteArrayInputStream(previewBytes); LinkedHashMap code = new LinkedHashMap<>(); try { code.put(watchfaceName, context.getAssets().open("fossil_hr/openSourceWatchface.bin")); @@ -163,6 +170,7 @@ public class HybridHRWatchfaceFactory { LinkedHashMap icons = new LinkedHashMap<>(); try { icons.put("background.raw", backgroundStream); + icons.put("!preview.rle", previewStream); icons.put("icTrophy", context.getAssets().open("fossil_hr/icTrophy.rle")); if (includeWidget("widgetWeather") > 0) icons.put("icWthClearDay", context.getAssets().open("fossil_hr/icWthClearDay.rle")); if (includeWidget("widgetWeather") > 0) icons.put("icWthClearNite", context.getAssets().open("fossil_hr/icWthClearNite.rle")); @@ -254,4 +262,104 @@ public class HybridHRWatchfaceFactory { return configuration.toString(); } + + public static HybridHRWatchfaceWidget parseWidgetJSON(JSONObject widgetJSON) throws JSONException { + HybridHRWatchfaceWidget parsedWidget = null; + String widgetName = widgetJSON.getString("name"); + String widgetTimezone = null; + int widgetUpdateTimeout = -1; + boolean widgetTimeoutHideText = true; + boolean widgetTimeoutShowCircle = true; + switch (widgetName) { + case "dateSSE": + widgetName = "widgetDate"; + break; + case "weatherSSE": + widgetName = "widgetWeather"; + break; + case "stepsSSE": + widgetName = "widgetSteps"; + break; + case "hrSSE": + widgetName = "widgetHR"; + break; + case "batterySSE": + widgetName = "widgetBattery"; + break; + case "caloriesSSE": + widgetName = "widgetCalories"; + break; + case "activeMinutesSSE": + widgetName = "widgetActiveMins"; + break; + case "chanceOfRainSSE": + widgetName = "widgetChanceOfRain"; + break; + case "timeZone2SSE": + widgetName = "widget2ndTZ"; + break; + } + int widgetColor = widgetJSON.getString("color").equals("white") ? HybridHRWatchfaceWidget.COLOR_WHITE : HybridHRWatchfaceWidget.COLOR_BLACK; + if (widgetName.startsWith("widget2ndTZ")) { + try { + widgetName = "widget2ndTZ"; + JSONObject widgetData = widgetJSON.getJSONObject("data"); + widgetTimezone = widgetData.getString("tzName"); + parsedWidget = new HybridHRWatchfaceWidget(widgetName, + widgetJSON.getJSONObject("pos").getInt("x"), + widgetJSON.getJSONObject("pos").getInt("y"), + widgetJSON.getJSONObject("size").getInt("w"), + widgetJSON.getJSONObject("size").getInt("h"), + widgetColor, + widgetTimezone); + } catch (JSONException e) { + LOG.error("Couldn't determine tzName!", e); + } + } else if (widgetName.startsWith("widgetCustom")) { + widgetName = "widgetCustom"; + JSONObject widgetData = widgetJSON.getJSONObject("data"); + widgetUpdateTimeout = widgetData.getInt("update_timeout"); + widgetTimeoutHideText = widgetData.getBoolean("timeout_hide_text"); + widgetTimeoutShowCircle = widgetData.getBoolean("timeout_show_circle"); + parsedWidget = new HybridHRWatchfaceWidget(widgetName, + widgetJSON.getJSONObject("pos").getInt("x"), + widgetJSON.getJSONObject("pos").getInt("y"), + widgetJSON.getJSONObject("size").getInt("w"), + widgetJSON.getJSONObject("size").getInt("h"), + widgetColor, + widgetUpdateTimeout, + widgetTimeoutHideText, + widgetTimeoutShowCircle); + } else { + parsedWidget = new HybridHRWatchfaceWidget(widgetName, + widgetJSON.getJSONObject("pos").getInt("x"), + widgetJSON.getJSONObject("pos").getInt("y"), + widgetJSON.getJSONObject("size").getInt("w"), + widgetJSON.getJSONObject("size").getInt("h"), + widgetColor); + } + return parsedWidget; + } + + public Bitmap getPreviewImage(Context context) { + if (previewImage == null) { + previewImage = BitmapUtil.getCircularBitmap(background); + Canvas previewCanvas = new Canvas(previewImage); + int widgetSize = 50; + float scaleFactor = previewImage.getWidth() / 240; + for (int i=0; i