mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-25 01:55:50 +01:00
Merge pull request #445 from Freeyourgadget/liveview
Liveview: Initial support for Liveview devices
This commit is contained in:
commit
259eb51784
@ -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
|
||||
|
@ -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 (<a
|
||||
* href="http://adq.livejournal.com">http://adq.livejournal.com</a>).
|
||||
*
|
||||
* @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";
|
||||
|
||||
}
|
@ -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<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> 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<? extends ActivitySample> 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<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
// nothing to delete, yet
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ public enum DeviceType {
|
||||
MIBAND(10),
|
||||
MIBAND2(11),
|
||||
VIBRATISSIMO(20),
|
||||
LIVEVIEW(30),
|
||||
TEST(1000);
|
||||
|
||||
private final int key;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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<? extends Alarm> 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user