Merge pull request #445 from Freeyourgadget/liveview

Liveview: Initial support for Liveview devices
here
Daniele Gobbetti 2016-12-07 13:21:40 +01:00 committed by GitHub
commit 259eb51784
10 changed files with 688 additions and 2 deletions

View File

@ -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

View File

@ -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 &lt;xperimental@solidproject.de&gt;
*/
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";
}

View File

@ -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
}
}

View File

@ -12,6 +12,7 @@ public enum DeviceType {
MIBAND(10),
MIBAND2(11),
VIBRATISSIMO(20),
LIVEVIEW(30),
TEST(1000);
private final int key;

View File

@ -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

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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
}
}

View File

@ -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;
}