diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java index f3beb1853..b477a2821 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java @@ -532,6 +532,10 @@ public class QHybridSupport extends QHybridBaseSupport { } public void notifiyException(Exception e){ + notifiyException("", e); + } + + public void notifiyException(String requestName, Exception e){ StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); @@ -544,7 +548,7 @@ public class QHybridSupport extends QHybridBaseSupport { notificationBuilder = new Notification.Builder(getContext()); } notificationBuilder - .setContentTitle("Q Error") + .setContentTitle("Q Error " + requestName) .setSmallIcon(R.drawable.ic_notification_qhybrid) .setContentText(sStackTrace) .setStyle(new Notification.BigTextStyle()) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java index 7c8ad243c..3d3d67710 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java @@ -460,7 +460,7 @@ public class FossilWatchAdapter extends WatchAdapter { requestFinished = fossilRequest.isFinished(); } catch (RuntimeException e) { GB.log("error", GB.ERROR, e); - getDeviceSupport().notifiyException(e); + getDeviceSupport().notifiyException(fossilRequest.getName(), e); GB.toast(fossilRequest.getName() + " failed", Toast.LENGTH_SHORT, GB.ERROR); requestFinished = true; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java index 85a782787..df3ce805e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java @@ -19,10 +19,12 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Random; import java.util.TimeZone; import java.util.zip.CRC32; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.Widget; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.HRConfigActivity; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; @@ -34,11 +36,13 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.encoder.RLEE import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.RequestMtuRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.SetDeviceStateRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.*; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileDeleteRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayNotificationRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication.VerifyPrivateKeyRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.buttons.ButtonConfigurationPutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.configuration.ConfigurationGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.configuration.ConfigurationPutRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.AssetFile; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.AssetFilePutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImage; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImageFactory; @@ -46,6 +50,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.menu.SetCommuteMenuMessage; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicInfoSetRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.CustomWidget; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.CustomWidgetElement; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.utils.StringUtils; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest.*; @@ -55,8 +61,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { private byte[] phoneRandomNumber; private byte[] watchRandomNumber; + CustomWidget[] widgets; + private MusicSpec currentSpec = null; + int imageNameIndex = 0; + public FossilHRWatchAdapter(QHybridSupport deviceSupport) { super(deviceSupport); } @@ -86,62 +96,89 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { overwriteButtons(null); - drawWidgetText("-"); + queueWrite(new FileDeleteRequest((short) 0x0700)); + + loadWidgets(); + renderWidgets(); + // dunno if there is any point in doing this at start since when no watch is connected the QHybridSupport will not receive any intents anyway queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED)); } - @Override - public void setWidgetContent(String widgetID, String content) { - drawWidgetText(content); + private void loadWidgets() { + CustomWidget ethWidget = new CustomWidget(0, 63); + ethWidget.addElement(new CustomWidgetElement(CustomWidgetElement.WidgetElementType.TYPE_TEXT, "date", "-", CustomWidgetElement.X_CENTER, CustomWidgetElement.Y_UPPER_HALF)); + ethWidget.addElement(new CustomWidgetElement(CustomWidgetElement.WidgetElementType.TYPE_TEXT, "eth", "-", CustomWidgetElement.X_CENTER, CustomWidgetElement.Y_LOWER_HALF)); + + + CustomWidget btcWidget = new CustomWidget(90, 63); + btcWidget.addElement(new CustomWidgetElement(CustomWidgetElement.WidgetElementType.TYPE_TEXT, "", "BTC", CustomWidgetElement.X_CENTER, CustomWidgetElement.Y_UPPER_HALF)); + btcWidget.addElement(new CustomWidgetElement(CustomWidgetElement.WidgetElementType.TYPE_TEXT, "btc", "wtf", CustomWidgetElement.X_CENTER, CustomWidgetElement.Y_LOWER_HALF)); + this.widgets = new CustomWidget[]{ + ethWidget, + // btcWidget, + }; } - private void drawWidgetText(String text){ - + private void renderWidgets() { try { - Bitmap testBitmap = Bitmap.createBitmap(76, 76, Bitmap.Config.ARGB_8888); + AssetImage[] widgetImages = new AssetImage[this.widgets.length]; - Canvas testCanvas = new Canvas(testBitmap); + for (int i = 0; i < this.widgets.length; i++) { + CustomWidget widget = widgets[i]; - Paint circlePaint = new Paint(); - circlePaint.setColor(Color.WHITE); - circlePaint.setStyle(Paint.Style.STROKE); - circlePaint.setStrokeWidth(3); + Bitmap widgetBitmap = Bitmap.createBitmap(76, 76, Bitmap.Config.ARGB_8888); - testCanvas.drawCircle(38, 38, 37, circlePaint); + Canvas widgetCanvas = new Canvas(widgetBitmap); - circlePaint.setStrokeWidth(4); - circlePaint.setTextSize(17f); - circlePaint.setStyle(Paint.Style.FILL); - circlePaint.setTextAlign(Paint.Align.CENTER); + Paint circlePaint = new Paint(); + circlePaint.setColor(Color.WHITE); + circlePaint.setStyle(Paint.Style.STROKE); + circlePaint.setStrokeWidth(3); - testCanvas.drawText("ETH", 38, 76f / 3f * 1f - (circlePaint.descent() + circlePaint.ascent()) / 2f, circlePaint); - testCanvas.drawText(text, 38, 76f / 3f * 2f - (circlePaint.descent() + circlePaint.ascent()) / 2f, circlePaint); + widgetCanvas.drawCircle(38, 38, 37, circlePaint); - circlePaint.setStrokeWidth(1); - circlePaint.setStyle(Paint.Style.STROKE); + for (CustomWidgetElement element : widget.getElements()) { + if (element.getWidgetElementType() == CustomWidgetElement.WidgetElementType.TYPE_TEXT) { + Paint textPaint = new Paint(); + textPaint.setStrokeWidth(4); + textPaint.setTextSize(17f); + textPaint.setStyle(Paint.Style.FILL); + textPaint.setColor(Color.WHITE); + textPaint.setTextAlign(Paint.Align.CENTER); - // for(int i = 0; i <= 3; i++) testCanvas.drawLine(0, 76f / 3f * i - (i / 3), 76, 76f / 3f * i - (i / 3), circlePaint); + widgetCanvas.drawText(element.getValue(), element.getX(), element.getY() - (textPaint.descent() + textPaint.ascent()) / 2f, textPaint); + } + } + widgetImages[i] = AssetImageFactory.createAssetImage( + StringUtils.bytesToHex( + ByteBuffer.allocate(8) + .putLong(System.currentTimeMillis() + imageNameIndex++) + .array() + ) + , + widgetBitmap, + true, + widget.getAngle(), + widget.getDistance(), + 1 + ); + } - AssetImage image = AssetImageFactory.createAssetImage( - StringUtils.bytesToHex( - ByteBuffer.allocate(4) - .putInt((int) System.currentTimeMillis()) - .array() - ) - , testBitmap, true, 0, 60, 1); - - AssetImage[] images = new AssetImage[]{ - image, - }; queueWrite(new AssetFilePutRequest( - images, + new AssetFile[]{widgetImages[0]}, this )); + // for(int i = 0; i < widgetImages.length; i++){ + // queueWrite(new AssetFilePutRequest(widgetImages[i], i + 1, this)); + // } + + // widgetImages[1].setFileName(widgetImages[0].getFileName()); + queueWrite(new ImagesSetRequest( - images, + widgetImages, this )); } catch (IOException e) { @@ -149,6 +186,20 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { } } + @Override + public void setWidgetContent(String widgetID, String content) { + boolean update = false; + for(CustomWidget widget : this.widgets){ + CustomWidgetElement element = widget.getElement(widgetID); + if(element == null) continue; + + element.setValue(content); + update = true; + } + + if(update) renderWidgets(); + } + private void negotiateSymmetricKey() { queueWrite(new VerifyPrivateKeyRequest( this.getSecretKey(), @@ -279,32 +330,31 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { if (requestType == (byte) 0x05) { handleMusicRequest(value); - return; - } + }else if(requestType == (byte) 0x01) { + int eventId = value[2]; - int eventId = value[2]; + try { + JSONObject requestJson = new JSONObject(new String(value, 3, value.length - 3)); - try { - JSONObject requestJson = new JSONObject(new String(value, 3, value.length - 3)); + String action = requestJson.getJSONObject("req").getJSONObject("commuteApp._.config.commute_info") + .getString("dest"); - String action = requestJson.getJSONObject("req").getJSONObject("commuteApp._.config.commute_info") - .getString("dest"); + String startStop = requestJson.getJSONObject("req").getJSONObject("commuteApp._.config.commute_info") + .getString("action"); - String startStop = requestJson.getJSONObject("req").getJSONObject("commuteApp._.config.commute_info") - .getString("action"); + if (startStop.equals("stop")) { + // overwriteButtons(null); + return; + } - if (startStop.equals("stop")) { - // overwriteButtons(null); - return; + queueWrite(new SetCommuteMenuMessage("Anfrage wird weitergeleitet...", false, this)); + + Intent menuIntent = new Intent(QHybridSupport.QHYBRID_EVENT_COMMUTE_MENU); + menuIntent.putExtra("EXTRA_ACTION", action); + getContext().sendBroadcast(menuIntent); + } catch (JSONException e) { + e.printStackTrace(); } - - queueWrite(new SetCommuteMenuMessage("Anfrage wird weitergeleitet...", false, this)); - - Intent menuIntent = new Intent(QHybridSupport.QHYBRID_EVENT_COMMUTE_MENU); - menuIntent.putExtra("EXTRA_ACTION", action); - getContext().sendBroadcast(menuIntent); - } catch (JSONException e) { - e.printStackTrace(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileDeleteRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileDeleteRequest.java index 37ea92c44..e52daba88 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileDeleteRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileDeleteRequest.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode; public class FileDeleteRequest extends FossilRequest { private boolean finished = false; @@ -53,7 +54,8 @@ public class FileDeleteRequest extends FossilRequest { if(buffer.getShort(1) != this.handle) throw new RuntimeException("wrong response handle"); - if(buffer.get(3) != 0) throw new RuntimeException("wrong response status: " + buffer.get(3)); + byte status = buffer.get(3); + if(status != 0) throw new RuntimeException("wrong response status: " + ResultCode.fromCode(status) + "(" + status + ")"); this.finished = true; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFile.java index cab96d4e9..189dd5b9d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFile.java @@ -16,4 +16,8 @@ public class AssetFile { public byte[] getFileData() { return fileData; } + + public void setFileName(String fileName) { + this.fileName = fileName; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java index 0ecf6e8e7..90487c4fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java @@ -17,6 +17,10 @@ public class AssetFilePutRequest extends FilePutRequest { super((short) 0x0700, prepareFileData(file), adapter); } + public AssetFilePutRequest(AssetFile file, int subHandle, FossilWatchAdapter adapter) throws IOException { + super((short) (0x0700 | subHandle), prepareFileData(file), adapter); + } + private static byte[] prepareFileData(AssetFile[] files) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/CustomWidget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/CustomWidget.java new file mode 100644 index 000000000..1ffbbd545 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/CustomWidget.java @@ -0,0 +1,40 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +public class CustomWidget { + private HashMap elements = new HashMap<>(); + private int angle, distance; + + public CustomWidget(int angle, int distance) { + this.angle = angle; + this.distance = distance; + } + + public int getAngle() { + return angle; + } + + public int getDistance() { + return distance; + } + + public Collection getElements(){ + return this.elements.values(); + } + + public void addElement(CustomWidgetElement element){ + this.elements.put(element.getId(), element); + } + + public CustomWidgetElement getElement(String id){ + return elements.get(id); + } + + public CustomWidgetElement removeElement(String id){ + return elements.remove(id); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/CustomWidgetElement.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/CustomWidgetElement.java new file mode 100644 index 000000000..670554a8d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/CustomWidgetElement.java @@ -0,0 +1,49 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget; + +public class CustomWidgetElement { + public enum WidgetElementType { + TYPE_TEXT, + TYPE_IMAGE, + TYPE_BACKGROUND + } + + public final static int X_CENTER = 38; + public final static int Y_UPPER_HALF = (int) (76f / 3 * 1); + public final static int Y_LOWER_HALF = (int) (76f / 3 * 2); + + private WidgetElementType widgetElementType; + private String id, value; + private int x, y; + + public CustomWidgetElement(WidgetElementType widgetElementType, String id, String value, int x, int y) { + this.widgetElementType = widgetElementType; + this.id = id; + this.value = value; + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public WidgetElementType getWidgetElementType() { + return widgetElementType; + } + + public String getId() { + return id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +}