Fossil Hybrid HR: Add optional circle backgrounds to widgets
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.1 KiB |
BIN
app/src/main/assets/fossil_hr/widget_bg_dashed_circle.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
app/src/main/assets/fossil_hr/widget_bg_double_circle.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/assets/fossil_hr/widget_bg_thin_circle.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
@ -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();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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>() {
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
2
external/fossil-hr-watchface
vendored
@ -1 +1 @@
|
||||
Subproject commit 3b35b2a0a6bc0e8a5262e7b61154cdf487fa94a6
|
||||
Subproject commit 84b3c55cda7515761600daaebe79582c6e0d4686
|