From 9a32be97cb7b3a39fcca23e16ab04a3944b9c154 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 13 Sep 2015 18:20:15 +0200 Subject: [PATCH] Pebble: work towards PebbleKit support #106 - Untested features have to be turned on. - We will accept data from any source. - One way, we do not send out replies. This already works with the minimalistic sports demo from the sdk --- .../devices/pebble/PebbleIoThread.java | 80 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 31 ++++++- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 76a71483..99f788b0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -3,12 +3,17 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.net.Uri; import android.os.ParcelUuid; import android.preference.PreferenceManager; +import org.json.JSONArray; +import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,6 +25,7 @@ import java.net.InetAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.UUID; import java.util.zip.ZipInputStream; import nodomain.freeyourgadget.gadgetbridge.R; @@ -38,6 +44,18 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; public class PebbleIoThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class); + + public static final String PEBBLEKIT_ACTION_PEBBLE_CONNECTED = "com.getpebble.action.PEBBLE_CONNECTED"; + public static final String PEBBLEKIT_ACTION_PEBBLE_DISCONNECTED = "com.getpebble.action.PEBBLE_DISCONNECTED"; + public static final String PEBBLEKIT_ACTION_APP_ACK = "com.getpebble.action.app.ACK"; + public static final String PEBBLEKIT_ACTION_APP_NACK = "com.getpebble.action.app.NACK"; + public static final String PEBBLEKIT_ACTION_APP_RECEIVE = "com.getpebble.action.app.RECEIVE"; + public static final String PEBBLEKIT_ACTION_APP_RECEIVE_ACK = "com.getpebble.action.app.RECEIVE_ACK"; + public static final String PEBBLEKIT_ACTION_APP_RECEIVE_NACK = "com.getpebble.action.app.RECEIVE_NACK"; + public static final String PEBBLEKIT_ACTION_APP_SEND = "com.getpebble.action.app.SEND"; + public static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; + public static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; + private final PebbleProtocol mPebbleProtocol; private final PebbleSupport mPebbleSupport; private boolean mIsTCP = false; @@ -62,6 +80,35 @@ public class PebbleIoThread extends GBDeviceIoThread { private int mBinarySize = -1; private int mBytesWritten = -1; + private final BroadcastReceiver mPebbleKitReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + LOG.info("Got action: " + action); + UUID uuid; + switch (action) { + case PEBBLEKIT_ACTION_APP_START: + uuid = (UUID) intent.getSerializableExtra("uuid"); + if (uuid != null) { + write(mPebbleProtocol.encodeAppStart(uuid)); + } + break; + case PEBBLEKIT_ACTION_APP_SEND: + uuid = (UUID) intent.getSerializableExtra("uuid"); + String jsonString = intent.getStringExtra("msg_data"); + LOG.info("json string: " + jsonString); + + try { + JSONArray jsonArray = new JSONArray(jsonString); + write(mPebbleProtocol.encodeApplicationMessageFromJSON(uuid, jsonArray)); + } catch (JSONException e) { + e.printStackTrace(); + } + break; + } + } + }; + public PebbleIoThread(PebbleSupport pebbleSupport, GBDevice gbDevice, GBDeviceProtocol gbDeviceProtocol, BluetoothAdapter btAdapter, Context context) { super(gbDevice, context); mPebbleProtocol = (PebbleProtocol) gbDeviceProtocol; @@ -116,6 +163,7 @@ public class PebbleIoThread extends GBDeviceIoThread { gbDevice.sendDeviceUpdateIntent(getContext()); mIsConnected = connect(gbDevice.getAddress()); + enablePebbleKitReceiver(mIsConnected); mQuit = !mIsConnected; // quit if not connected byte[] buffer = new byte[8192]; @@ -285,11 +333,43 @@ public class PebbleIoThread extends GBDeviceIoThread { e.printStackTrace(); } } + enablePebbleKitReceiver(false); mBtSocket = null; gbDevice.setState(GBDevice.State.NOT_CONNECTED); gbDevice.sendDeviceUpdateIntent(getContext()); } + private void enablePebbleKitReceiver(boolean enable) { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + boolean force_untested = sharedPrefs.getBoolean("pebble_force_untested", false); + + if (enable && force_untested) { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_ACK); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_NACK); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_RECEIVE); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_RECEIVE_ACK); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_RECEIVE_NACK); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_SEND); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_START); + intentFilter.addAction(PEBBLEKIT_ACTION_APP_STOP); + intentFilter.addAction(PEBBLEKIT_ACTION_PEBBLE_CONNECTED); + intentFilter.addAction(PEBBLEKIT_ACTION_PEBBLE_DISCONNECTED); + try { + getContext().registerReceiver(mPebbleKitReceiver, intentFilter); + } catch (IllegalArgumentException e) { + // ignore + } + } else { + try { + getContext().unregisterReceiver(mPebbleKitReceiver); + } catch (IllegalArgumentException e) { + // ignore + } + } + } + + private void write_real(byte[] bytes) { try { if (mIsTCP) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index e338ff83..a8c3b2f6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -2,6 +2,9 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.util.Pair; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,8 +16,6 @@ import java.util.Random; import java.util.SimpleTimeZone; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement; @@ -24,9 +25,11 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificati import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.model.NotificationKind; +import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; public class PebbleProtocol extends GBDeviceProtocol { @@ -621,7 +624,7 @@ public class PebbleProtocol extends GBDeviceProtocol { color_id = PebbleColor.VividViolet; break; default: - switch(notificationKind){ + switch (notificationKind) { case TWITTER: icon_id = PebbleIconID.NOTIFICATION_TWITTER; color_id = PebbleColor.BlueMoon; @@ -1173,6 +1176,26 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } + public byte[] encodeApplicationMessageFromJSON(UUID uuid, JSONArray jsonArray) { + ArrayList> pairs = new ArrayList<>(); + for (int i = 0; i < jsonArray.length(); i++) { + try { + JSONObject jsonObject = (JSONObject) jsonArray.get(i); + String type = (String) jsonObject.get("type"); + int key = (int) jsonObject.get("key"); + if (type.equals("uint") || type.equals("int")) { + pairs.add(new Pair<>(key, (Object) jsonObject.getInt("value"))); + } else if (type.equals("string")) { + pairs.add(new Pair<>(key, (Object) jsonObject.getString("value"))); + } + } catch (JSONException e) { + return null; + } + } + + return encodeApplicationMessagePush(ENDPOINT_APPLICATIONMESSAGE, uuid, pairs); + } + private static byte reverseBits(byte in) { byte out = 0; for (int i = 0; i < 8; i++) {