diff --git a/README.md b/README.md index 00892367f..b6252aaa4 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ need to create an account and transmit any of your data to the vendor's servers. * Mi Band, Mi Band 1A, Mi Band 1S * Mi Band 2 * Vibratissimo (experimental) +* Liveview ## Features (Pebble) @@ -113,6 +114,12 @@ Known Issues: * If you use other apps like Mi Fit, and "bonding" with Gadgetbridge does not work, please try to unpair the band in the other app and try again with Gadgetbridge. + +## Features (Liveview) + +* set time (automatically upon connection) +* display notifications and vibrate + ## Authors (in order of first code contribution) * Andreas Shimokawa diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java new file mode 100644 index 000000000..48786c241 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java @@ -0,0 +1,110 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.liveview; +//Changed by Renze: Fixed brightness constants + +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Message constants reverse-engineered by Andrew de Quincey (http://adq.livejournal.com). + * + * @author Robert <xperimental@solidproject.de> + */ +public final class LiveviewConstants { + + public static Charset ENCODING = StandardCharsets.ISO_8859_1; + public static ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN; + + public static final byte CLOCK_24H = 0; + public static final byte CLOCK_12H = 1; + + public static final byte MSG_GETCAPS = 1; + public static final byte MSG_GETCAPS_RESP = 2; + + public static final byte MSG_DISPLAYTEXT = 3; + public static final byte MSG_DISPLAYTEXT_ACK = 4; + + public static final byte MSG_DISPLAYPANEL = 5; + public static final byte MSG_DISPLAYPANEL_ACK = 6; + + public static final byte MSG_DEVICESTATUS = 7; + public static final byte MSG_DEVICESTATUS_ACK = 8; + + public static final byte MSG_DISPLAYBITMAP = 19; + public static final byte MSG_DISPLAYBITMAP_ACK = 20; + + public static final byte MSG_CLEARDISPLAY = 21; + public static final byte MSG_CLEARDISPLAY_ACK = 22; + + public static final byte MSG_SETMENUSIZE = 23; + public static final byte MSG_SETMENUSIZE_ACK = 24; + + public static final byte MSG_GETMENUITEM = 25; + public static final byte MSG_GETMENUITEM_RESP = 26; + + public static final byte MSG_GETALERT = 27; + public static final byte MSG_GETALERT_RESP = 28; + + public static final byte MSG_NAVIGATION = 29; + public static final byte MSG_NAVIGATION_RESP = 30; + + public static final byte MSG_SETSTATUSBAR = 33; + public static final byte MSG_SETSTATUSBAR_ACK = 34; + + public static final byte MSG_GETMENUITEMS = 35; + + public static final byte MSG_SETMENUSETTINGS = 36; + public static final byte MSG_SETMENUSETTINGS_ACK = 37; + + public static final byte MSG_GETTIME = 38; + public static final byte MSG_GETTIME_RESP = 39; + + public static final byte MSG_SETLED = 40; + public static final byte MSG_SETLED_ACK = 41; + + public static final byte MSG_SETVIBRATE = 42; + public static final byte MSG_SETVIBRATE_ACK = 43; + + public static final byte MSG_ACK = 44; + + public static final byte MSG_SETSCREENMODE = 64; + public static final byte MSG_SETSCREENMODE_ACK = 65; + + public static final byte MSG_GETSCREENMODE = 66; + public static final byte MSG_GETSCREENMODE_RESP = 67; + + public static final int DEVICESTATUS_OFF = 0; + public static final int DEVICESTATUS_ON = 1; + public static final int DEVICESTATUS_MENU = 2; + + public static final byte RESULT_OK = 0; + public static final byte RESULT_ERROR = 1; + public static final byte RESULT_OOM = 2; + public static final byte RESULT_EXIT = 3; + public static final byte RESULT_CANCEL = 4; + + public static final int NAVACTION_PRESS = 0; + public static final int NAVACTION_LONGPRESS = 1; + public static final int NAVACTION_DOUBLEPRESS = 2; + + public static final int NAVTYPE_UP = 0; + public static final int NAVTYPE_DOWN = 1; + public static final int NAVTYPE_LEFT = 2; + public static final int NAVTYPE_RIGHT = 3; + public static final int NAVTYPE_SELECT = 4; + public static final int NAVTYPE_MENUSELECT = 5; + + public static final int ALERTACTION_CURRENT = 0; + public static final int ALERTACTION_FIRST = 1; + public static final int ALERTACTION_LAST = 2; + public static final int ALERTACTION_NEXT = 3; + public static final int ALERTACTION_PREV = 4; + + public static final int BRIGHTNESS_OFF = 49; + public static final int BRIGHTNESS_DIM = 50; + public static final int BRIGHTNESS_MAX = 51; + + public static final String CLIENT_SOFTWARE_VERSION = "0.0.3"; + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java new file mode 100644 index 000000000..338137cef --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java @@ -0,0 +1,105 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.liveview; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class LiveviewCoordinator extends AbstractDeviceCoordinator { + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + if (name != null && name.startsWith("LiveView")) { + return DeviceType.LIVEVIEW; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.LIVEVIEW; + } + + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public Class getPrimaryActivity() { + return null; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsAlarmConfiguration() { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public int getTapString() { + //TODO: changeme + return R.string.tap_connected_device_for_activity; + } + + @Override + public String getManufacturer() { + return "Sony Ericsson"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + // nothing to delete, yet + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 2a1c859eb..5c9d26636 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -12,6 +12,7 @@ public enum DeviceType { MIBAND(10), MIBAND2(11), VIBRATISSIMO(20), + LIVEVIEW(30), TEST(1000); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 74e74f81c..8c25a793f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -174,7 +174,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere try (DBHandler dbHandler = GBApplication.acquireDB()) { DaoSession session = dbHandler.getDaoSession(); boolean askForDBMigration = false; - if (DBHelper.findDevice(device, session) == null && device.getType() != DeviceType.VIBRATISSIMO) { + if (DBHelper.findDevice(device, session) == null && device.getType() != DeviceType.VIBRATISSIMO && (device.getType() != DeviceType.LIVEVIEW)) { askForDBMigration = true; } DBHelper.getDevice(device, session); // implicitly creates the device in database if not present, and updates device attributes diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 4b05efa6e..7b3098186 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -9,8 +9,8 @@ import java.util.EnumSet; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; @@ -93,6 +93,9 @@ public class DeviceSupportFactory { case VIBRATISSIMO: deviceSupport = new ServiceDeviceSupport(new VibratissimoSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case LIVEVIEW: + deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java new file mode 100644 index 000000000..2034a07fa --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java @@ -0,0 +1,220 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.liveview; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.os.ParcelUuid; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewConstants; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class LiveviewIoThread extends GBDeviceIoThread { + private static final Logger LOG = LoggerFactory.getLogger(LiveviewIoThread.class); + + private static final UUID SERIAL = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); + + private final LiveviewProtocol mLiveviewProtocol; + private final LiveviewSupport mLiveviewSupport; + + + private BluetoothAdapter mBtAdapter = null; + private BluetoothSocket mBtSocket = null; + private InputStream mInStream = null; + private OutputStream mOutStream = null; + private boolean mQuit = false; + + @Override + public void quit() { + mQuit = true; + if (mBtSocket != null) { + try { + mBtSocket.close(); + } catch (IOException e) { + LOG.error(e.getMessage()); + } + } + } + + private boolean mIsConnected = false; + + + public LiveviewIoThread(GBDevice gbDevice, Context context, GBDeviceProtocol lvProtocol, LiveviewSupport lvSupport, BluetoothAdapter lvBtAdapter) { + super(gbDevice, context); + mLiveviewProtocol = (LiveviewProtocol) lvProtocol; + mBtAdapter = lvBtAdapter; + mLiveviewSupport = lvSupport; + } + + @Override + public synchronized void write(byte[] bytes) { + if (null == bytes) + return; + LOG.debug("writing:" + GB.hexdump(bytes, 0, bytes.length)); + try { + mOutStream.write(bytes); + mOutStream.flush(); + } catch (IOException e) { + LOG.error("Error writing.", e); + } + } + + @Override + public void run() { + mIsConnected = connect(); + if (!mIsConnected) { + setUpdateState(GBDevice.State.NOT_CONNECTED); + return; + } + mQuit = false; + + while (!mQuit) { + LOG.info("Ready for a new message exchange."); + + try { + GBDeviceEvent deviceEvents[] = mLiveviewProtocol.decodeResponse(parseIncoming()); + if (deviceEvents == null) { + LOG.info("unhandled message"); + } else { + for (GBDeviceEvent deviceEvent : deviceEvents) { + if (deviceEvent == null) { + continue; + } + mLiveviewSupport.evaluateGBDeviceEvent(deviceEvent); + } + } + } catch (SocketTimeoutException ignore) { + LOG.debug("socket timeout, we can't help but ignore this"); + } catch (IOException e) { + LOG.info(e.getMessage()); + mIsConnected = false; + mBtSocket = null; + mInStream = null; + mOutStream = null; + LOG.info("Bluetooth socket closed, will quit IO Thread"); + break; + } + } + + mIsConnected = false; + if (mBtSocket != null) { + try { + mBtSocket.close(); + } catch (IOException e) { + LOG.error(e.getMessage()); + } + mBtSocket = null; + } + setUpdateState(GBDevice.State.NOT_CONNECTED); + } + + @Override + protected boolean connect() { + GBDevice.State originalState = gbDevice.getState(); + setUpdateState(GBDevice.State.CONNECTING); + + try { + BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(gbDevice.getAddress()); + ParcelUuid uuids[] = btDevice.getUuids(); + if (uuids == null) { + return false; + } + for (ParcelUuid uuid : uuids) { + LOG.info("found service UUID " + uuid); + } + mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); + mBtSocket.connect(); + mInStream = mBtSocket.getInputStream(); + mOutStream = mBtSocket.getOutputStream(); + setUpdateState(GBDevice.State.CONNECTED); + } catch (IOException e) { + LOG.error("Server socket cannot be started."); + //LOG.error(e.getMessage()); + setUpdateState(originalState); + mInStream = null; + mOutStream = null; + mBtSocket = null; + return false; + } + + write(mLiveviewProtocol.encodeSetTime()); + setUpdateState(GBDevice.State.INITIALIZED); + + return true; + } + + private void setUpdateState(GBDevice.State state) { + gbDevice.setState(state); + gbDevice.sendDeviceUpdateIntent(getContext()); + } + + private byte[] parseIncoming() throws IOException { + ByteArrayOutputStream msgStream = new ByteArrayOutputStream(); + + boolean finished = false; + ReaderState state = ReaderState.ID; + byte[] incoming = new byte[1]; + + while (!finished) { + mInStream.read(incoming); + msgStream.write(incoming); + + switch (state) { + case ID: + state = ReaderState.HEADER_LEN; + incoming = new byte[1]; + break; + case HEADER_LEN: + int headerSize = 0xff & incoming[0]; + if (headerSize < 0) + throw new IOException(); + state = ReaderState.HEADER; + incoming = new byte[headerSize]; + break; + case HEADER: + int payloadSize = getLastInt(msgStream); + if (payloadSize < 0 || payloadSize > 8000) //this will possibly be changed in the future + throw new IOException(); + state = ReaderState.PAYLOAD; + incoming = new byte[payloadSize]; + break; + case PAYLOAD: //read is blocking, if we are here we have all the data + finished = true; + break; + } + } + byte[] msgArray = msgStream.toByteArray(); + LOG.debug("received: " + GB.hexdump(msgArray, 0, msgArray.length)); + return msgArray; + } + + + /** + * Enumeration containing the possible internal status of the reader. + */ + private enum ReaderState { + ID, HEADER_LEN, HEADER, PAYLOAD; + } + + private int getLastInt(ByteArrayOutputStream stream) { + byte[] array = stream.toByteArray(); + ByteBuffer buffer = ByteBuffer.wrap(array, array.length - 4, 4); + buffer.order(LiveviewConstants.BYTE_ORDER); + return buffer.getInt(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java new file mode 100644 index 000000000..27d38d7cd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java @@ -0,0 +1,132 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.liveview; + +import java.nio.ByteBuffer; +import java.util.Calendar; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewConstants; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; + +public class LiveviewProtocol extends GBDeviceProtocol { + + @Override + public byte[] encodeFindDevice(boolean start) { + return encodeVibrateRequest((short) 100, (short) 200); + } + + protected LiveviewProtocol(GBDevice device) { + super(device); + } + + @Override + public GBDeviceEvent[] decodeResponse(byte[] responseData) { + int length = responseData.length; + if (length < 4) { + //empty message + return null; + } else { + ByteBuffer buffer = ByteBuffer.wrap(responseData, 0, length); + byte msgId = buffer.get(); + buffer.get(); + int payloadLen = buffer.getInt(); + GBDeviceEventSendBytes reply = new GBDeviceEventSendBytes(); + if (payloadLen + 6 == length) { + switch (msgId) { + case LiveviewConstants.MSG_DEVICESTATUS: + reply.encodedBytes = constructMessage(LiveviewConstants.MSG_DEVICESTATUS_ACK, new byte[]{LiveviewConstants.RESULT_OK}); + break; + case LiveviewConstants.MSG_DISPLAYPANEL_ACK: + reply.encodedBytes = encodeVibrateRequest((short) 100, (short) 200); //hack to make the notifications vibrate! + break; + default: + } + GBDeviceEventSendBytes ack = new GBDeviceEventSendBytes(); + ack.encodedBytes = constructMessage(LiveviewConstants.MSG_ACK, new byte[]{msgId}); + + return new GBDeviceEvent[]{ack, reply}; + } + } + + + return super.decodeResponse(responseData); + } + + @Override + public byte[] encodeSetTime() { + int time = (int) (Calendar.getInstance().getTimeInMillis() / 1000); + time += Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 1000; + time += Calendar.getInstance().get(Calendar.DST_OFFSET) / 1000; + ByteBuffer buffer = ByteBuffer.allocate(5); + buffer.order(LiveviewConstants.BYTE_ORDER); + buffer.putInt(time); + buffer.put(LiveviewConstants.CLOCK_24H); + return constructMessage(LiveviewConstants.MSG_GETTIME_RESP, buffer.array()); + } + + @Override + public byte[] encodeNotification(NotificationSpec notificationSpec) { + String headerText; + // for SMS and EMAIL that came in though SMS or K9 receiver + if (notificationSpec.sender != null) { + headerText = notificationSpec.sender; + } else { + headerText = notificationSpec.title; + } + + String footerText = (null != notificationSpec.sourceName) ? notificationSpec.sourceName : ""; + String bodyText = (null != notificationSpec.body) ? notificationSpec.body : ""; + + byte[] headerTextArray = headerText.getBytes(LiveviewConstants.ENCODING); + byte[] footerTextArray = footerText.getBytes(LiveviewConstants.ENCODING); + byte[] bodyTextArray = bodyText.getBytes(LiveviewConstants.ENCODING); + int size = 15 + headerTextArray.length + bodyTextArray.length + footerTextArray.length; + ByteBuffer buffer = ByteBuffer.allocate(size); + buffer.put((byte) 1); + buffer.putShort((short) 0); + buffer.putShort((short) 0); + buffer.putShort((short) 0); + buffer.put((byte) 80); //should alert but it doesn't make the liveview vibrate + + buffer.put((byte) 0); //0 is for plaintext vs bitmapimage (1) strings + buffer.putShort((short) headerTextArray.length); + buffer.put(headerTextArray); + buffer.putShort((short) bodyTextArray.length); + buffer.put(bodyTextArray); + buffer.putShort((short) footerTextArray.length); + buffer.put(footerTextArray); + return constructMessage(LiveviewConstants.MSG_DISPLAYPANEL, buffer.array()); + } + + + //specific messages + + public static byte[] constructMessage(byte messageType, byte[] payload) { + ByteBuffer msgBuffer = ByteBuffer.allocate(payload.length + 6); + msgBuffer.order(LiveviewConstants.BYTE_ORDER); + msgBuffer.put(messageType); + msgBuffer.put((byte) 4); + msgBuffer.putInt(payload.length); + msgBuffer.put(payload); + return msgBuffer.array(); + } + + public byte[] encodeVibrateRequest(short delay, short time) { + ByteBuffer buffer = ByteBuffer.allocate(4); + buffer.order(LiveviewConstants.BYTE_ORDER); + buffer.putShort(delay); + buffer.putShort(time); + return constructMessage(LiveviewConstants.MSG_SETVIBRATE, buffer.array()); + } + + public byte[] encodeCapabilitiesRequest() { + byte[] version = LiveviewConstants.CLIENT_SOFTWARE_VERSION.getBytes(LiveviewConstants.ENCODING); + ByteBuffer buffer = ByteBuffer.allocate(version.length + 1); + buffer.order(LiveviewConstants.BYTE_ORDER); + buffer.put((byte) version.length); + buffer.put(version); + return constructMessage(LiveviewConstants.MSG_GETCAPS, buffer.array()); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java new file mode 100644 index 000000000..08842be7b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java @@ -0,0 +1,106 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.liveview; + +import android.net.Uri; + +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; + +public class LiveviewSupport extends AbstractSerialDeviceSupport { + + @Override + public boolean connect() { + getDeviceIOThread().start(); + return true; + } + + @Override + protected GBDeviceProtocol createDeviceProtocol() { + return new LiveviewProtocol(getDevice()); + } + + @Override + protected GBDeviceIoThread createDeviceIOThread() { + return new LiveviewIoThread(getDevice(), getContext(), getDeviceProtocol(), LiveviewSupport.this, getBluetoothAdapter()); + } + + @Override + public boolean useAutoConnect() { + return false; + } + + @Override + public void onInstallApp(Uri uri) { + //nothing to do ATM + } + + @Override + public void onAppConfiguration(UUID uuid, String config) { + //nothing to do ATM + } + + @Override + public void onHeartRateTest() { + //nothing to do ATM + } + + @Override + public void onSetConstantVibration(int intensity) { + //nothing to do ATM + } + + @Override + public synchronized LiveviewIoThread getDeviceIOThread() { + return (LiveviewIoThread) super.getDeviceIOThread(); + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + super.onNotification(notificationSpec); + } + + @Override + public void onSetCallState(CallSpec callSpec) { + //nothing to do ATM + } + + @Override + public void onSetMusicState(MusicStateSpec musicStateSpec) { + //nothing to do ATM + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + //nothing to do ATM + } + + + @Override + public void onSetAlarms(ArrayList alarms) { + //nothing to do ATM + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + //nothing to do ATM + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + //nothing to do ATM + } + + @Override + public void onTestNewFunction() { + //nothing to do ATM + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index b16445a8f..0b0c6a8d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -22,6 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; @@ -165,6 +166,7 @@ public class DeviceHelper { result.add(new MiBandCoordinator()); result.add(new PebbleCoordinator()); result.add(new VibratissimoCoordinator()); + result.add(new LiveviewCoordinator()); return result; }