mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-02-07 15:36:47 +01:00
Merge remote-tracking branch 'freeyourgadget/master'
This commit is contained in:
commit
c7351156ca
@ -160,10 +160,8 @@ android {
|
||||
resValue "string", "about_activity_title", "@string/about_activity_title_banglejs_main"
|
||||
resValue "string", "about_description", "@string/about_description_banglejs_main"
|
||||
resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_banglejs_main"
|
||||
targetSdkVersion 30 // SDK 30 needed for play store
|
||||
// SDK 30 stops us querying package names - https://developer.android.com/training/package-visibility
|
||||
// which we need for getting app name from notifications. We have to request android.permission.QUERY_ALL_PACKAGES
|
||||
// as well for SDK 30
|
||||
targetSdkVersion 30 // Bangle.js flavor only - We need SDK 30 for play store
|
||||
// Note: app/src/banglejs/AndroidManifest.xml contains some extra permissions we need to make SDK 30 work
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,4 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
||||
|
||||
<!-- SDK 30 https://developer.android.com/training/package-visibility for getting app name from notifications -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
</manifest>
|
||||
|
Binary file not shown.
@ -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);
|
||||
|
@ -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<JSONObject> 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<String, InputStream> code = new LinkedHashMap<>();
|
||||
try {
|
||||
code.put(watchfaceName, context.getAssets().open("fossil_hr/openSourceWatchface.bin"));
|
||||
@ -163,6 +170,7 @@ public class HybridHRWatchfaceFactory {
|
||||
LinkedHashMap<String, InputStream> 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<widgets.size(); i++) {
|
||||
try {
|
||||
HybridHRWatchfaceWidget parsedWidget = parseWidgetJSON(widgets.get(i));
|
||||
Bitmap widgetPreview = Bitmap.createScaledBitmap(parsedWidget.getPreviewImage(context), (int)(widgetSize * scaleFactor), (int)(widgetSize * scaleFactor), true);
|
||||
int offsetFromCenter = (int)((widgetSize/2) * scaleFactor);
|
||||
previewCanvas.drawBitmap(widgetPreview, parsedWidget.getPosX() - offsetFromCenter, parsedWidget.getPosY() - offsetFromCenter, null);
|
||||
} catch (JSONException e) {
|
||||
LOG.warn("Couldn't parse widget JSON", e);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Couldn't parse widget JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return previewImage;
|
||||
}
|
||||
}
|
||||
|
@ -1584,6 +1584,18 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
|
||||
);
|
||||
queueWrite(new JsonPutRequest(responseObject, this));
|
||||
}
|
||||
} else if (request.optString("custom_menu").equals("request_config")) {
|
||||
// watchface requests custom menu data to be initialized
|
||||
LOG.info("Got custom_menu config request, sending intent to HR Menu Companion app...");
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClassName("d.d.hrmenucompanion", "d.d.hrmenucompanion.MainActivity");
|
||||
intent.putExtra("SEND_CONFIG", true);
|
||||
try {
|
||||
getContext().startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
LOG.info("Couldn't send intent to Fossil-HR-Menu-Companion app, is it installed?");
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Unhandled request from watch: " + requestJson.toString());
|
||||
}
|
||||
|
2
external/fossil-hr-watchface
vendored
2
external/fossil-hr-watchface
vendored
@ -1 +1 @@
|
||||
Subproject commit f07ed376e9046dbcc9c5d7821117c80b2d79ffd1
|
||||
Subproject commit 6a0c2bdad14e58d4cf4359e5f573c60aa89bca53
|
Loading…
x
Reference in New Issue
Block a user