From 8ba7bc7353ea16c631841fc91a781b76e409710f Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 19 Sep 2016 12:37:41 +0200 Subject: [PATCH] Add (useless) support for Vibratissimo "massage devices" Don't take this serious. It will make the "massage device" vibrate when a phone call arrives. It is inspired by the famous lawsuit[1] which has nothing to do with the Vibratissimo device maker. After reading this I picked up the cheapest ble massage device just to see if we could support it. And yes, we can. [1] http://arstechnica.com/wp-content/uploads/2016/09/vibratorsuit.pdf --- .../vibratissimo/VibratissimoCoordinator.java | 81 ++++++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 5 + .../vibratissimo/VibratissimoSupport.java | 275 ++++++++++++++++++ .../gadgetbridge/util/DeviceHelper.java | 2 + 5 files changed, 364 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java new file mode 100644 index 00000000..d10a69c0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java @@ -0,0 +1,81 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; + +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.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class VibratissimoCoordinator extends AbstractDeviceCoordinator { + public VibratissimoCoordinator() { + } + + @Override + public boolean supports(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + return name != null && name.startsWith("Vibratissimo"); + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.VIBRATISSIMO; + } + + @Override + public Class getPairingActivity() { + return null; + } + + public Class getPrimaryActivity() { + return null; + } + + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return true; + } + + @Override + public boolean supportsAlarmConfiguration() { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public int getTapString() { + return R.string.tap_connected_device_for_activity; + } + + @Override + public String getManufacturer() { + return "Armor Gummiwaren GmbH"; + } +} 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 caf561ee..43223637 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -11,6 +11,7 @@ public enum DeviceType { PEBBLE(1), MIBAND(10), MIBAND2(11), + VIBRATISSIMO(20), TEST(1000); private final int key; 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 0658b729..4b05efa6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -9,10 +9,12 @@ 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.miband.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DeviceSupportFactory { @@ -88,6 +90,9 @@ public class DeviceSupportFactory { case MIBAND2: deviceSupport = new ServiceDeviceSupport(new MiBand2Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case VIBRATISSIMO: + deviceSupport = new ServiceDeviceSupport(new VibratissimoSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, 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/vibratissimo/VibratissimoSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java new file mode 100644 index 00000000..4da7583d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java @@ -0,0 +1,275 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.support.v4.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; + +public class VibratissimoSupport extends AbstractBTLEDeviceSupport { + + private static final Logger LOG = LoggerFactory.getLogger(VibratissimoSupport.class); + private final DeviceInfoProfile deviceInfoProfile; + private final BatteryInfoProfile batteryInfoProfile; + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String s = intent.getAction(); + if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) { + handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO)); + } else if (s.equals(BatteryInfoProfile.ACTION_BATTERY_INFO)) { + handleBatteryInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo) intent.getParcelableExtra(BatteryInfoProfile.EXTRA_BATTERY_INFO)); + } + } + }; + private BtLEQueue mQueue; + + public VibratissimoSupport() { + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); + addSupportedService(GattService.UUID_SERVICE_DEVICE_INFORMATION); + addSupportedService(UUID.fromString("00001523-1212-efde-1523-785feabcd123")); + + deviceInfoProfile = new DeviceInfoProfile<>(this); + batteryInfoProfile = new BatteryInfoProfile<>(this); + addSupportedProfile(deviceInfoProfile); + addSupportedProfile(batteryInfoProfile); + + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BatteryInfoProfile.ACTION_BATTERY_INFO); + intentFilter.addAction(DeviceInfoProfile.ACTION_DEVICE_INFO); + broadcastManager.registerReceiver(mReceiver, intentFilter); + } + + private void handleBatteryInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo info) { + batteryCmd.level = (short) info.getPercentCharged(); + handleGBDeviceEvent(batteryCmd); + } + + @Override + public void dispose() { + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + broadcastManager.unregisterReceiver(mReceiver); + super.dispose(); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); + requestDeviceInfo(builder); + setInitialized(builder); + return builder; + } + + private void requestDeviceInfo(TransactionBuilder builder) { + LOG.debug("Requesting Device Info!"); + deviceInfoProfile.requestDeviceInfo(builder); + } + + private void setInitialized(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + } + + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + public void pair() { + + } + + private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { + LOG.warn("Device info: " + info); + versionCmd.hwVersion = info.getHardwareRevision(); + versionCmd.fwVersion = info.getFirmwareRevision(); + handleGBDeviceEvent(versionCmd); + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + } + + @Override + public void onSetTime() { + + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onSetCallState(CallSpec callSpec) { + BluetoothGattCharacteristic characteristic2 = getCharacteristic(UUID.fromString("00001526-1212-efde-1523-785feabcd123")); + BluetoothGattCharacteristic characteristic1 = getCharacteristic(UUID.fromString("00001524-1212-efde-1523-785feabcd123")); + + TransactionBuilder builder = new TransactionBuilder("phonetest"); + builder.write(characteristic1, new byte[]{0x03, (byte) 0x80}); + + byte intensity; + if (callSpec.command == CallSpec.CALL_INCOMING) { + intensity = 0x65; + builder.write(characteristic2, new byte[]{0x65, 0x00}); + } else { + intensity = 0; + } + builder.write(characteristic2, new byte[]{intensity, 0x00}); + builder.queue(getQueue()); + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppInfoReq() { + + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + + } + + @Override + public void onAppDelete(UUID uuid) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchActivityData() { + + } + + @Override + public void onReboot() { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + + } + + @Override + public void onFindDevice(boolean start) { + + } + + @Override + public void onScreenshotReq() { + + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + + } + + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + + UUID characteristicUUID = characteristic.getUuid(); + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + return false; + } + + @Override + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + if (super.onCharacteristicRead(gatt, characteristic, status)) { + return true; + } + UUID characteristicUUID = characteristic.getUuid(); + + LOG.info("Unhandled characteristic read: " + characteristicUUID); + return false; + + + } +} 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 118203d5..346be2fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -24,6 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -180,6 +181,7 @@ public class DeviceHelper { result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm result.add(new MiBandCoordinator()); result.add(new PebbleCoordinator()); + result.add(new VibratissimoCoordinator()); return result; }