1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-22 06:41:06 +02:00

Fossil Hybrid HR: Add optional circle backgrounds to widgets

This commit is contained in:
Arjan Schrijver 2022-07-24 22:32:53 +02:00
parent c946a045ef
commit 7d5fe20b55
24 changed files with 141 additions and 22 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -416,7 +416,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
}
private void renderWatchfacePreview() {
int widgetSize = 50;
int widgetSize = QHybridConstants.HYBRID_HR_WATCHFACE_WIDGET_SIZE;
if (selectedBackgroundImage == null) {
try {
selectedBackgroundImage = BitmapUtil.getCircularBitmap(BitmapFactory.decodeStream(getAssets().open("fossil_hr/default_background.png")));
@ -502,7 +502,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
posY = newPosition.posY;
GB.toast(getString(R.string.watchface_dialog_pre_setting_position, getString(newPosition.hintStringResource)), Toast.LENGTH_SHORT, GB.INFO);
}
int color = 0;
int color = defaultWidgetColor;
if (widgets.size() > 0) {
color = widgets.get(0).getColor();
}

View File

@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import org.json.JSONArray;
@ -69,32 +70,19 @@ public class HybridHRWatchfaceFactory {
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.getExtraConfigInt("update_timeout", -1) >= 0) {
JSONObject data = new JSONObject();
data.put("update_timeout", widgetDesc.getExtraConfigInt("update_timeout", -1));
data.put("timeout_hide_text", widgetDesc.getExtraConfigBoolean("timeout_hide_text", true));
data.put("timeout_show_circle", widgetDesc.getExtraConfigBoolean("timeout_show_circle", true));
if (widgetDesc.getBackground() != "") {
data.put("background", widgetDesc.getBackground() + widgetDesc.getColor() + ".rle");
}
widget.put("data", data);
}
break;
// fall through
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.getExtraConfigString("tzName", null) != null) {
JSONObject data = new JSONObject();
TimeZone tz = TimeZone.getTimeZone(widgetDesc.getExtraConfigString("tzName", null));
@ -106,6 +94,22 @@ public class HybridHRWatchfaceFactory {
data.put("timeout_secs", widgetDesc.getExtraConfigInt("timeout_secs", 0));
widget.put("data", data);
}
// fall through
case "widgetDate":
case "widgetWeather":
case "widgetSteps":
case "widgetHR":
case "widgetBattery":
case "widgetCalories":
case "widgetActiveMins":
case "widgetChanceOfRain":
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.getBackground() != "") {
widget.put("bg", widgetDesc.getBackground() + widgetDesc.getColor() + ".rle");
}
break;
default:
LOG.warn("Invalid widget name: " + widgetDesc.getWidgetType());
@ -144,6 +148,26 @@ public class HybridHRWatchfaceFactory {
return count;
}
private Boolean includeBackground(String name, int color) {
for (JSONObject widget : this.widgets) {
try {
if (widget.get("bg").toString().startsWith(name + color)) {
return true;
}
} catch (JSONException e) {
}
}
return false;
}
private InputStream getWidgetBackgroundStream(Context context, String name, Boolean invert) throws IOException {
Bitmap bgImage = BitmapFactory.decodeStream(context.getAssets().open("fossil_hr/" + name + ".png"));
if (invert) {
bgImage = BitmapUtil.invertBitmapColors(bgImage);
}
return new ByteArrayInputStream(ImageConverter.encodeToRLEImage(ImageConverter.get2BitsRLEImageBytes(bgImage), QHybridConstants.HYBRID_HR_WATCHFACE_WIDGET_SIZE, QHybridConstants.HYBRID_HR_WATCHFACE_WIDGET_SIZE));
}
public byte[] getWapp(Context context) throws IOException {
byte[] backgroundBytes = ImageConverter.encodeToRawImage(ImageConverter.get2BitsRAWImageBytes(background));
InputStream backgroundStream = new ByteArrayInputStream(backgroundBytes);
@ -192,6 +216,26 @@ public class HybridHRWatchfaceFactory {
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"));
// Note: we have to check and invert every used widget background here,
// because the watch doesn't invert the background image when the widget color is inverted
if (includeBackground("widget_bg_thin_circle", HybridHRWatchfaceWidget.COLOR_WHITE)) {
icons.put("widget_bg_thin_circle" + HybridHRWatchfaceWidget.COLOR_WHITE + ".rle", getWidgetBackgroundStream(context, "widget_bg_thin_circle", false));
}
if (includeBackground("widget_bg_thin_circle", HybridHRWatchfaceWidget.COLOR_BLACK)) {
icons.put("widget_bg_thin_circle" + HybridHRWatchfaceWidget.COLOR_BLACK + ".rle", getWidgetBackgroundStream(context, "widget_bg_thin_circle", true));
}
if (includeBackground("widget_bg_double_circle", HybridHRWatchfaceWidget.COLOR_WHITE)) {
icons.put("widget_bg_double_circle" + HybridHRWatchfaceWidget.COLOR_WHITE + ".rle", getWidgetBackgroundStream(context, "widget_bg_double_circle", false));
}
if (includeBackground("widget_bg_double_circle", HybridHRWatchfaceWidget.COLOR_BLACK)) {
icons.put("widget_bg_double_circle" + HybridHRWatchfaceWidget.COLOR_BLACK + ".rle", getWidgetBackgroundStream(context, "widget_bg_double_circle", true));
}
if (includeBackground("widget_bg_dashed_circle", HybridHRWatchfaceWidget.COLOR_WHITE)) {
icons.put("widget_bg_dashed_circle" + HybridHRWatchfaceWidget.COLOR_WHITE + ".rle", getWidgetBackgroundStream(context, "widget_bg_dashed_circle", false));
}
if (includeBackground("widget_bg_dashed_circle", HybridHRWatchfaceWidget.COLOR_BLACK)) {
icons.put("widget_bg_dashed_circle" + HybridHRWatchfaceWidget.COLOR_BLACK + ".rle", getWidgetBackgroundStream(context, "widget_bg_dashed_circle", true));
}
} catch (IOException e) {
LOG.warn("Unable to read asset file", e);
}
@ -315,6 +359,10 @@ public class HybridHRWatchfaceFactory {
widgetJSON.getJSONObject("size").getInt("h"),
widgetColor,
widgetData);
String widgetBackground = widgetJSON.optString("bg", "");
if (widgetBackground != "") {
parsedWidget.setBackground(widgetBackground.replaceAll("[0-9]?\\.rle$", ""));
}
return parsedWidget;
}
@ -322,7 +370,7 @@ public class HybridHRWatchfaceFactory {
if (previewImage == null) {
previewImage = BitmapUtil.getCircularBitmap(background);
Canvas previewCanvas = new Canvas(previewImage);
int widgetSize = 50;
int widgetSize = QHybridConstants.HYBRID_HR_WATCHFACE_WIDGET_SIZE;
float scaleFactor = previewImage.getWidth() / 240;
for (int i=0; i<widgets.size(); i++) {
try {

View File

@ -27,6 +27,7 @@ import java.io.Serializable;
import java.util.LinkedHashMap;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
import static nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil.invertBitmapColors;
@ -37,6 +38,7 @@ public class HybridHRWatchfaceWidget implements Serializable {
private int width;
private int height;
private int color;
private String background;
private String extraConfigJSON;
public static int COLOR_WHITE = 0;
@ -56,6 +58,7 @@ public class HybridHRWatchfaceWidget implements Serializable {
this.width = width;
this.height = height;
this.color = color;
this.background = "";
try {
this.extraConfigJSON = extraConfig.toString();
} catch (Exception e) {
@ -88,6 +91,14 @@ public class HybridHRWatchfaceWidget implements Serializable {
public Bitmap getPreviewImage(Context context) throws IOException {
Bitmap preview = BitmapFactory.decodeStream(context.getAssets().open("fossil_hr/" + widgetType + "_preview.png"));
if (getBackground() != "") {
try {
Bitmap background = BitmapFactory.decodeStream(context.getAssets().open("fossil_hr/" + getBackground() + ".png"));
preview = BitmapUtil.overlay(background, preview);
} catch (Exception e) {
// continue silently without background
}
}
if (color == COLOR_WHITE) {
return preview;
} else {
@ -130,6 +141,13 @@ public class HybridHRWatchfaceWidget implements Serializable {
this.color = color;
}
public String getBackground() {
return background;
}
public void setBackground(String background) {
this.background = background;
}
public int getExtraConfigInt(String name, int fallback) {
try {
return new JSONObject(extraConfigJSON).optInt(name, fallback);

View File

@ -110,6 +110,11 @@ public class HybridHRWatchfaceWidgetActivity extends AbstractSettingsActivity {
widgetColor.setValueIndex(widget.getColor());
widgetColor.setSummary(widgetColors[widget.getColor()]);
ListPreference widgetBg = (ListPreference) findPreference("pref_hybridhr_widget_background");
widgetBg.setOnPreferenceChangeListener(this);
widgetBg.setValue(widget.getBackground());
widgetBg.setSummary(widgetBg.getEntry());
EditTextPreference posX = (EditTextPreference) findPreference("pref_hybridhr_widget_pos_x");
posX.setOnPreferenceChangeListener(this);
posX.setText(Integer.toString(widget.getPosX()));
@ -181,6 +186,11 @@ public class HybridHRWatchfaceWidgetActivity extends AbstractSettingsActivity {
widget.setColor(Integer.parseInt(newValue.toString()));
preference.setSummary(widgetColors[widget.getColor()]);
break;
case "pref_hybridhr_widget_background":
widget.setBackground(newValue.toString());
((ListPreference)preference).setValue(newValue.toString());
preference.setSummary(((ListPreference)preference).getEntry());
break;
case "pref_hybridhr_widget_pos_x":
widget.setPosX(Integer.parseInt(newValue.toString()));
preference.setSummary(newValue.toString());

View File

@ -20,7 +20,8 @@ import java.util.HashMap;
import java.util.Map;
public final class QHybridConstants {
public static final String HYBRIDHR_WATCHFACE_VERSION = "1.1";
public static final String HYBRIDHR_WATCHFACE_VERSION = "1.2";
public static final int HYBRID_HR_WATCHFACE_WIDGET_SIZE = 76;
public static Map<String, String> KNOWN_WAPP_VERSIONS = new HashMap<String, String>() {
{

View File

@ -190,4 +190,21 @@ public class BitmapUtil {
matrix.postRotate(degree);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
* Overlays two bitmaps on top of each other,
* bmp1 is assumed to be larger or equal to bmp2
* From: https://stackoverflow.com/a/2287218
* @param bmp1
* @param bmp2
* @return new Bitmap
*/
public static Bitmap overlay(Bitmap bmp1, Bitmap bmp2) {
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, new Matrix(), null);
canvas.drawBitmap(bmp2, new Matrix(), null);
return bmOverlay;
}
}

View File

@ -1811,6 +1811,19 @@
<item>workoutApp</item>
</string-array>
<string-array name="pref_hybridhr_widgetbackgrounds_items">
<item name="">@string/menuitem_nothing</item>
<item name="widget_bg_thin_circle">@string/hybridhr_widget_bg_thin_circle</item>
<item name="widget_bg_double_circle">@string/hybridhr_widget_bg_double_circle</item>
<item name="widget_bg_dashed_circle">@string/hybridhr_widget_bg_dashed_circle</item>
</string-array>
<string-array name="pref_hybridhr_widgetbackgrounds_values">
<item></item>
<item>widget_bg_thin_circle</item>
<item>widget_bg_double_circle</item>
<item>widget_bg_dashed_circle</item>
</string-array>
<string-array name="activity_filter_quick_filter_period_items">
<item>@string/sports_activity_quick_filter_select</item>
<item>@string/sports_activity_quick_filter_this_week</item>

View File

@ -1478,6 +1478,10 @@
<string name="watchface_setting_title_power_saving">Power saving</string>
<string name="watchface_setting_power_saving_display">Disable display updates while off wrist</string>
<string name="watchface_setting_power_saving_hands">Disable hands movement while off wrist</string>
<string name="watchface_dialog_widget_background">Background</string>
<string name="hybridhr_widget_bg_thin_circle">Thin circle</string>
<string name="hybridhr_widget_bg_double_circle">Double circle</string>
<string name="hybridhr_widget_bg_dashed_circle">Dashed circle</string>
<string name="prefs_sleep_time">Sleep times</string>
<string name="prefs_sleep_time_label">Define sleep hours</string>
<string name="prefs_sleep_time_summary">Specifies times when sleep is registered</string>

View File

@ -17,6 +17,14 @@
android:key="pref_hybridhr_widget_color"
android:dialogTitle="@string/watchface_dialog_widget_color"
android:negativeButtonText="@string/Cancel"/>
<ListPreference
android:persistent="false"
android:title="@string/watchface_dialog_widget_background"
android:key="pref_hybridhr_widget_background"
android:dialogTitle="@string/watchface_dialog_widget_background"
android:entries="@array/pref_hybridhr_widgetbackgrounds_items"
android:entryValues="@array/pref_hybridhr_widgetbackgrounds_values"
android:negativeButtonText="@string/Cancel"/>
</PreferenceCategory>
<PreferenceCategory

@ -1 +1 @@
Subproject commit 3b35b2a0a6bc0e8a5262e7b61154cdf487fa94a6
Subproject commit 84b3c55cda7515761600daaebe79582c6e0d4686