1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-10 08:16:48 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java

258 lines
13 KiB
Java
Raw Normal View History

/* Copyright (C) 2021 Arjan Schrijver
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
import android.content.Context;
import android.graphics.Bitmap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.TimeZone;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.ImageConverter;
public class HybridHRWatchfaceFactory {
private final Logger LOG = LoggerFactory.getLogger(HybridHRWatchfaceFactory.class);
private String watchfaceName;
private HybridHRWatchfaceSettings settings;
private Bitmap background;
private ArrayList<JSONObject> widgets = new ArrayList<>();
public HybridHRWatchfaceFactory(String name) {
watchfaceName = name.replaceAll("[^-A-Za-z0-9]", "");
if (watchfaceName.equals("")) throw new AssertionError("name cannot be empty");
if (watchfaceName.endsWith("App")) watchfaceName += "Watchface";
}
public void setSettings(HybridHRWatchfaceSettings settings) {
this.settings = settings;
}
public void setBackground(Bitmap background) {
if ((background.getWidth() == 240) && (background.getHeight() == 240)) {
this.background = background;
} else {
this.background = Bitmap.createScaledBitmap(background, 240, 240, true);
}
}
public void addWidget(HybridHRWatchfaceWidget widgetDesc) {
JSONObject widget = new JSONObject();
try {
switch (widgetDesc.getWidgetType()) {
case "widgetDate":
case "widgetWeather":
case "widgetSteps":
case "widgetHR":
case "widgetBattery":
case "widgetCalories":
case "widgetActiveMins":
case "widgetChanceOfRain":
case "widgetCustom":
widget.put("type", "comp");
widget.put("name", widgetDesc.getWidgetType());
widget.put("goal_ring", false);
widget.put("color", widgetDesc.getColor() == HybridHRWatchfaceWidget.COLOR_WHITE ? "white" : "black");
if (widgetDesc.getUpdateTimeout() >= 0) {
JSONObject data = new JSONObject();
data.put("update_timeout", widgetDesc.getUpdateTimeout());
data.put("timeout_hide_text", widgetDesc.getTimeoutHideText());
data.put("timeout_show_circle", widgetDesc.getTimeoutShowCircle());
widget.put("data", data);
}
break;
case "widget2ndTZ":
widget.put("type", "comp");
widget.put("name", widgetDesc.getWidgetType());
widget.put("goal_ring", false);
widget.put("color", widgetDesc.getColor() == HybridHRWatchfaceWidget.COLOR_WHITE ? "white" : "black");
if (widgetDesc.getTimezone() != null) {
JSONObject data = new JSONObject();
TimeZone tz = TimeZone.getTimeZone(widgetDesc.getTimezone());
String tzShortName = widgetDesc.getTimezone().replaceAll(".*/", "");
int tzOffsetMins = tz.getRawOffset() / 1000 / 60;
data.put("tzName", widgetDesc.getTimezone());
data.put("loc", tzShortName);
data.put("utc", tzOffsetMins);
widget.put("data", data);
}
break;
default:
LOG.warn("Invalid widget name: " + widgetDesc.getWidgetType());
return;
}
JSONObject size = new JSONObject();
size.put("w", widgetDesc.getWidth());
size.put("h", widgetDesc.getHeight());
widget.put("size", size);
JSONObject pos = new JSONObject();
pos.put("x", widgetDesc.getPosX());
pos.put("y", widgetDesc.getPosY());
widget.put("pos", pos);
widgets.add(widget);
} catch (JSONException e) {
LOG.warn("JSON error", e);
}
}
public void addWidgets(ArrayList<HybridHRWatchfaceWidget> widgets) {
for (HybridHRWatchfaceWidget widget : widgets) {
addWidget(widget);
}
}
private int includeWidget(String name) {
int count = 0;
for (JSONObject widget : this.widgets) {
try {
if (widget.get("name").equals(name)) {
count++;
}
} catch (JSONException e) {
}
}
return count;
}
public byte[] getWapp(Context context) throws IOException {
byte[] backgroundBytes = ImageConverter.encodeToRawImage(ImageConverter.get2BitsRAWImageBytes(background));
InputStream backgroundStream = new ByteArrayInputStream(backgroundBytes);
LinkedHashMap<String, InputStream> code = new LinkedHashMap<>();
try {
code.put(watchfaceName, context.getAssets().open("fossil_hr/openSourceWatchface.bin"));
if (includeWidget("widgetDate") > 0) code.put("widgetDate", context.getAssets().open("fossil_hr/widgetDate.bin"));
if (includeWidget("widgetWeather") > 0) code.put("widgetWeather", context.getAssets().open("fossil_hr/widgetWeather.bin"));
if (includeWidget("widgetSteps") > 0) code.put("widgetSteps", context.getAssets().open("fossil_hr/widgetSteps.bin"));
if (includeWidget("widgetHR") > 0) code.put("widgetHR", context.getAssets().open("fossil_hr/widgetHR.bin"));
if (includeWidget("widgetBattery") > 0) code.put("widgetBattery", context.getAssets().open("fossil_hr/widgetBattery.bin"));
if (includeWidget("widgetCalories") > 0) code.put("widgetCalories", context.getAssets().open("fossil_hr/widgetCalories.bin"));
if (includeWidget("widgetActiveMins") > 0) code.put("widgetActiveMins", context.getAssets().open("fossil_hr/widgetActiveMins.bin"));
if (includeWidget("widgetChanceOfRain") > 0) code.put("widgetChanceOfRain", context.getAssets().open("fossil_hr/widgetChanceOfRain.bin"));
for (int i=0; i<includeWidget("widget2ndTZ"); i++) {
code.put("widget2ndTZ" + i, context.getAssets().open("fossil_hr/widget2ndTZ.bin"));
}
for (int i=0; i<includeWidget("widgetCustom"); i++) {
code.put("widgetCustom" + i, context.getAssets().open("fossil_hr/widgetCustom.bin"));
}
} catch (IOException e) {
LOG.warn("Unable to read asset file", e);
}
LinkedHashMap<String, InputStream> icons = new LinkedHashMap<>();
try {
icons.put("background.raw", backgroundStream);
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"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthCloudy", context.getAssets().open("fossil_hr/icWthCloudy.rle"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthPartCloudyDay", context.getAssets().open("fossil_hr/icWthPartCloudyDay.rle"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthPartCloudyNite", context.getAssets().open("fossil_hr/icWthPartCloudyNite.rle"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthRainy", context.getAssets().open("fossil_hr/icWthRainy.rle"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthSnowy", context.getAssets().open("fossil_hr/icWthSnowy.rle"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthStormy", context.getAssets().open("fossil_hr/icWthStormy.rle"));
if (includeWidget("widgetWeather") > 0) icons.put("icWthWindy", context.getAssets().open("fossil_hr/icWthWindy.rle"));
if (includeWidget("widgetSteps") > 0) icons.put("icSteps", context.getAssets().open("fossil_hr/icSteps.rle"));
if (includeWidget("widgetHR") > 0) icons.put("icHeart", context.getAssets().open("fossil_hr/icHeart.rle"));
if (includeWidget("widgetBattery") > 0) icons.put("icBattCharging", context.getAssets().open("fossil_hr/icBattCharging.rle"));
if (includeWidget("widgetBattery") > 0) icons.put("icBattEmpty", context.getAssets().open("fossil_hr/icBattEmpty.rle"));
if (includeWidget("widgetBattery") > 0) icons.put("icBattery", context.getAssets().open("fossil_hr/icBattery.rle"));
if (includeWidget("widgetCalories") > 0) icons.put("icCalories", context.getAssets().open("fossil_hr/icCalories.rle"));
if (includeWidget("widgetActiveMins") > 0) icons.put("icActiveMins", context.getAssets().open("fossil_hr/icActiveMins.rle"));
if (includeWidget("widgetChanceOfRain") > 0) icons.put("icRainChance", context.getAssets().open("fossil_hr/icRainChance.rle"));
if (includeWidget("widgetCustom") > 0) icons.put("widget_bg_error.rle", context.getAssets().open("fossil_hr/widget_bg_error.rle"));
} catch (IOException e) {
LOG.warn("Unable to read asset file", e);
}
LinkedHashMap<String, InputStream> layout = new LinkedHashMap<>();
layout.put("complication_layout", context.getAssets().open("fossil_hr/complication_layout.json"));
layout.put("image_layout", context.getAssets().open("fossil_hr/image_layout.json"));
layout.put("menu_layout", context.getAssets().open("fossil_hr/menu_layout.json"));
if (includeWidget("widgetBattery") > 0) {
layout.put("battery_layout", context.getAssets().open("fossil_hr/battery_layout.json"));
}
LinkedHashMap<String, String> displayName = new LinkedHashMap<>();
displayName.put("display_name", watchfaceName);
displayName.put("theme_class", "complications");
LinkedHashMap<String, String> config = new LinkedHashMap<>();
try {
config.put("customWatchFace", getConfiguration());
} catch (JSONException e) {
LOG.warn("Could not generate configuration", e);
}
FossilAppWriter appWriter = new FossilAppWriter(context, "1.2.0.0", code, icons, layout, displayName, config);
return appWriter.getWapp();
}
private String getConfiguration() throws JSONException {
JSONObject configuration = new JSONObject();
JSONArray layout = new JSONArray();
JSONObject background = new JSONObject();
background.put("type", "image");
background.put("name", "background.raw");
JSONObject size = new JSONObject();
size.put("w", 240);
size.put("h", 240);
background.put("size", size);
JSONObject pos = new JSONObject();
pos.put("x", 120);
pos.put("y", 120);
background.put("pos", pos);
layout.put(background);
int count_widget2ndTZ = 0;
int count_widgetCustom = 0;
for (JSONObject widget : widgets) {
if (widget.get("name").equals("widget2ndTZ")) {
widget.put("name", "widget2ndTZ" + count_widget2ndTZ);
layout.put(widget);
count_widget2ndTZ++;
} else if (widget.get("name").equals("widgetCustom")) {
widget.put("name", "widgetCustom" + count_widgetCustom);
layout.put(widget);
count_widgetCustom++;
} else {
layout.put(widget);
}
}
configuration.put("layout", layout);
JSONObject config = new JSONObject();
config.put("timeout_display_full", settings.getDisplayTimeoutFull() * 60 * 1000);
config.put("timeout_display_partial", settings.getDisplayTimeoutPartial() * 60 * 1000);
config.put("wrist_flick_hands_relative", settings.isWristFlickHandsMoveRelative());
config.put("wrist_flick_duration", settings.getWristFlickDuration());
config.put("wrist_flick_move_hour", settings.getWristFlickMoveHour());
config.put("wrist_flick_move_minute", settings.getWristFlickMoveMinute());
config.put("light_up_on_notification", settings.getLightUpOnNotification());
config.put("powersave_display", settings.getPowersaveDisplay());
config.put("powersave_hands", settings.getPowersaveHands());
configuration.put("config", config);
return configuration.toString();
}
}