diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index c50de8312..495a92f7c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -44,6 +44,8 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_DEVICE_INTERNET_ACCESS = "device_internet_access"; public static final String PREF_DEVICE_INTENTS = "device_intents"; + public static final String PREF_BANGLEJS_TEXT_BITMAP = "banglejs_text_bitmap"; + public static final String PREF_DISCONNECT_NOTIFICATION = "disconnect_notification"; public static final String PREF_DISCONNECT_NOTIFICATION_START = "disconnect_notification_start"; public static final String PREF_DISCONNECT_NOTIFICATION_END = "disconnect_notification_end"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/banglejs/BangleJSCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/banglejs/BangleJSCoordinator.java index 50675ba29..59db90428 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/banglejs/BangleJSCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/banglejs/BangleJSCoordinator.java @@ -171,6 +171,7 @@ public class BangleJSCoordinator extends AbstractDeviceCoordinator { public int[] getSupportedDeviceSpecificSettings(GBDevice device) { Vector settings = new Vector(); + settings.add(R.xml.devicesettings_banglejs); settings.add(R.xml.devicesettings_transliteration); settings.add(R.xml.devicesettings_high_mtu); if (BuildConfig.INTERNET_ACCESS) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java index 4920dfad4..86fbef1aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/banglejs/BangleJSDeviceSupport.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -92,6 +93,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ALLOW_HIGH_MTU; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_INTERNET_ACCESS; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_INTENTS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BANGLEJS_TEXT_BITMAP; import static nodomain.freeyourgadget.gadgetbridge.database.DBHelper.*; import javax.xml.xpath.XPath; @@ -155,6 +157,7 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { LOG.info("UART TX: " + str); byte[] bytes; bytes = str.getBytes(StandardCharsets.ISO_8859_1); + // FIXME: somehow this is still giving us UTF8 data when we put images in strings. Maybe JSON.stringify is converting to UTF-8? for (int i=0;imtuSize) l=mtuSize; @@ -164,11 +167,33 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { } } + /// Write a string of data, and chunk it up + public String jsonToString(JSONObject jsonObj) { + String json = jsonObj.toString(); + // toString creates '\u0000' instead of '\0' + // FIXME: there have got to be nicer ways of handling this - maybe we just make our own JSON.toString (see below) + json = json.replaceAll("\\\\u000([01234567])", "\\\\$1"); + json = json.replaceAll("\\\\u00([0123456789abcdef][0123456789abcdef])", "\\\\x$1"); + return json; + /*String json = "{"; + Iterator iter = jsonObj.keys(); + while (iter.hasNext()) { + String key = iter.next(); + Object v = jsonObj.get(key); + if (v instanceof Integer) { + // ... + } else // .. + if (iter.hasNext()) json+=","; + } + return json+"}";*/ + } + + /// Write a JSON object of data private void uartTxJSON(String taskName, JSONObject json) { try { TransactionBuilder builder = performInitialized(taskName); - uartTx(builder, "\u0010GB("+json.toString()+")\n"); + uartTx(builder, "\u0010GB("+jsonToString(json)+")\n"); builder.queue(getQueue()); } catch (IOException e) { GB.toast(getContext(), "Error in "+taskName+": " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); @@ -196,10 +221,14 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { // JSON - we hope! try { JSONObject json = new JSONObject(line); + LOG.info("UART RX JSON parsed successfully"); handleUartRxJSON(json); } catch (JSONException e) { + LOG.info("UART RX JSON parse failure: "+ e.getLocalizedMessage()); GB.toast(getContext(), "Malformed JSON from Bangle.js: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); } + } else { + LOG.info("UART RX line started with "+(int)line.charAt(0)+" - ignoring"); } } @@ -424,6 +453,29 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { return true; } + + + public String renderUnicodeAsImage(String txt) { + if (txt==null) return null; + // If we're not doing conversion, pass this right back + Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress())); + if (!devicePrefs.getBoolean(PREF_BANGLEJS_TEXT_BITMAP, false)) + return txt; + // Otherwise split up and check each word + String words[] = txt.split(" "); + for (int i=0;i255) isRenderable = false; + } + if (!isRenderable) + words[i] = "\0"+bitmapToEspruinoString(textToBitmap(words[i])); + } + return String.join(" ", words); + } + @Override public void onNotification(NotificationSpec notificationSpec) { try { @@ -431,10 +483,10 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { o.put("t", "notify"); o.put("id", notificationSpec.getId()); o.put("src", notificationSpec.sourceName); - o.put("title", notificationSpec.title); - o.put("subject", notificationSpec.subject); - o.put("body", notificationSpec.body); - o.put("sender", notificationSpec.sender); + o.put("title", renderUnicodeAsImage(notificationSpec.title)); + o.put("subject", renderUnicodeAsImage(notificationSpec.subject)); + o.put("body", renderUnicodeAsImage(notificationSpec.body)); + o.put("sender", renderUnicodeAsImage(notificationSpec.sender)); o.put("tel", notificationSpec.phoneNumber); uartTxJSON("onNotification", o); } catch (JSONException e) { @@ -503,7 +555,7 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { cmdName = field.getName().substring(5).toLowerCase(); } catch (IllegalAccessException e) {} o.put("cmd", cmdName); - o.put("name", callSpec.name); + o.put("name", renderUnicodeAsImage(callSpec.name)); o.put("number", callSpec.number); uartTxJSON("onSetCallState", o); } catch (JSONException e) { @@ -540,9 +592,9 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { try { JSONObject o = new JSONObject(); o.put("t", "musicinfo"); - o.put("artist", musicSpec.artist); - o.put("album", musicSpec.album); - o.put("track", musicSpec.track); + o.put("artist", renderUnicodeAsImage(musicSpec.artist)); + o.put("album", renderUnicodeAsImage(musicSpec.album)); + o.put("track", renderUnicodeAsImage(musicSpec.track)); o.put("dur", musicSpec.duration); o.put("c", musicSpec.trackCount); o.put("n", musicSpec.trackNr); @@ -707,9 +759,23 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { } } + public Bitmap textToBitmap(String text) { + Paint paint = new Paint(0); // Paint.ANTI_ALIAS_FLAG not wanted as 1bpp + paint.setTextSize(18); + paint.setColor(0xFFFFFFFF); + paint.setTextAlign(Paint.Align.LEFT); + float baseline = -paint.ascent(); // ascent() is negative + int width = (int) (paint.measureText(text) + 0.5f); // round + int height = (int) (baseline + paint.descent() + 0.5f); + Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(image); + canvas.drawText(text, 0, baseline, paint); + return image; + } + /** Convert an Android bitmap to a base64 string for use in Espruino. * Currently only 1bpp, no scaling */ - public static String bitmapToEspruino(Bitmap bitmap) { + public static byte[] bitmapToEspruinoArray(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); byte bmp[] = new byte[((height * width + 7) >> 3) + 3]; @@ -732,7 +798,19 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport { if (cn > 0) bmp[n++] = (byte)c; //LOG.info("BMP: " + width + "x"+height+" n "+n); // Convert to base64 - return Base64.encodeToString(bmp, Base64.DEFAULT).replaceAll("\n",""); + return bmp; + } + + /** Convert an Android bitmap to a base64 string for use in Espruino. + * Currently only 1bpp, no scaling */ + public static String bitmapToEspruinoString(Bitmap bitmap) { + return new String(bitmapToEspruinoArray(bitmap), StandardCharsets.ISO_8859_1); + } + + /** Convert an Android bitmap to a base64 string for use in Espruino. + * Currently only 1bpp, no scaling */ + public static String bitmapToEspruinoBase64(Bitmap bitmap) { + return Base64.encodeToString(bitmapToEspruinoArray(bitmap), Base64.DEFAULT).replaceAll("\n",""); } /** Convert a drawable to a bitmap, for use with bitmapToEspruino */ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b58e6c1e2..41848b7b1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -180,6 +180,8 @@ Allow notifications from selected apps Transliteration Enable this if your device has no support for your language\'s font + Text as Bitmaps + If a word cannot be rendered with the watch\'s font, render it to a bitmap in Gadgetbridge and display the bitmap on the watch Right-To-Left Enable this if your device can not show right-to-left languages Right-To-Left Max Line Length diff --git a/app/src/main/res/xml/devicesettings_banglejs.xml b/app/src/main/res/xml/devicesettings_banglejs.xml new file mode 100644 index 000000000..9270f8baf --- /dev/null +++ b/app/src/main/res/xml/devicesettings_banglejs.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file