diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900Constants.java
new file mode 100644
index 000000000..65b7e5e2b
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900Constants.java
@@ -0,0 +1,70 @@
+/* Copyright (C) 2018 Andreas Böhler
+ based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900;
+
+import java.util.UUID;
+
+public final class CasioGB6900Constants {
+ public static final UUID CASIO_VIRTUAL_SERVER_SERVICE = UUID.fromString("26eb0007-b012-49a8-b1f8-394fb2032b0f");
+
+ public static final UUID CASIO_VIRTUAL_SERVER_FEATURES = UUID.fromString("26eb0008-b012-49a8-b1f8-394fb2032b0f");
+
+ public static final UUID CASIO_A_NOT_W_REQ_NOT = UUID.fromString( "26eb0009-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID CASIO_A_NOT_COM_SET_NOT = UUID.fromString( "26eb000a-b012-49a8-b1f8-394fb2032b0f");
+
+ public static final UUID CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
+
+ // Alert
+
+ public static final UUID ALERT_SERVICE_UUID = UUID.fromString("26eb0000-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID ALERT_CHARACTERISTIC_UUID = UUID.fromString("00002a46-0000-1000-8000-00805f9b34fb");
+ public static final UUID ALERT_NOTIFICATION_CONTROL_POINT = UUID.fromString("00002a44-0000-1000-8000-00805f9b34fb");
+
+ // Phone Alert
+ public static final UUID CASIO_PHONE_ALERT_STATUS_SERVICE = UUID.fromString("26eb0001-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID RINGER_CONTROL_POINT = UUID.fromString("00002a40-0000-1000-8000-00805f9b34fb");
+
+ // Phone Finder
+
+ public static final UUID CASIO_IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("26eb0005-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
+
+ // Current Time
+
+ public static final UUID CURRENT_TIME_SERVICE_UUID = UUID.fromString("26eb0002-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID CURRENT_TIME_CHARACTERISTIC_UUID = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb");
+ public static final UUID LOCAL_TIME_CHARACTERISTIC_UUID = UUID.fromString("00002a0f-0000-1000-8000-00805f9b34fb");
+
+ // Control Mode
+ public static final UUID WATCH_FEATURES_SERVICE_UUID = UUID.fromString("26eb000d-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID WATCH_CTRL_SERVICE_UUID = UUID.fromString("26eb0018-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID KEY_CONTAINER_CHARACTERISTIC_UUID = UUID.fromString("26eb0019-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID NAME_OF_APP_CHARACTERISTIC_UUID = UUID.fromString("26eb001d-b012-49a8-b1f8-394fb2032b0f");
+ public static final UUID FUNCTION_SWITCH_CHARACTERISTIC = UUID.fromString("26eb001e-b012-49a8-b1f8-394fb2032b0f");
+ public static final String MUSIC_MESSAGE = "Music";
+
+ // Notification Types
+
+ public static final byte CALL_NOTIFICATION_ID = 3;
+ public static final byte MAIL_NOTIFICATION_ID = 1;
+ public static final byte CALENDAR_NOTIFICATION_ID = 7;
+ public static final byte SNS_NOTIFICATION_ID = 13;
+ public static final byte SMS_NOTIFICATION_ID = 5;
+
+
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900DeviceCoordinator.java
new file mode 100644
index 000000000..f5d5cec04
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900DeviceCoordinator.java
@@ -0,0 +1,158 @@
+/* Copyright (C) 2018 Andreas Böhler
+ based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900;
+
+import android.app.Activity;
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import nodomain.freeyourgadget.gadgetbridge.GBException;
+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 CasioGB6900DeviceCoordinator extends AbstractDeviceCoordinator {
+ protected static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceCoordinator.class);
+
+ @NonNull
+ @Override
+ public DeviceType getSupportedType(GBDeviceCandidate candidate) {
+ if (candidate.supportsService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE)) {
+ return DeviceType.CASIOGB6900;
+ }
+
+ String name = candidate.getDevice().getName();
+ if (name != null) {
+ if (name.startsWith("CASIO")) {
+ return DeviceType.CASIOGB6900;
+ }
+ }
+
+ return DeviceType.UNKNOWN;
+ }
+
+ @Override
+ public int getBondingStyle(GBDevice deviceCandidate){
+ return BONDING_STYLE_BOND;
+ }
+
+ @Override
+ public boolean supportsCalendarEvents() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsRealtimeData() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsWeather() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsFindDevice() {
+ return true;
+ }
+
+ @Override
+ public DeviceType getDeviceType() {
+ return DeviceType.CASIOGB6900;
+ }
+
+ @Override
+ public Class extends Activity> getPairingActivity() {
+ 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 int getAlarmSlotCount() {
+ return 5; // 4 regular and one snooze but not yet implemented
+ }
+
+ @Override
+ public boolean supportsSmartWakeup(GBDevice device) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsHeartRateMeasurement(GBDevice device) {
+ return false;
+ }
+
+ @Override
+ public String getManufacturer() {
+ return "Casio";
+ }
+
+ @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 {
+
+ }
+}
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 de073474b..0ca18cfde 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
@@ -51,6 +51,7 @@ public enum DeviceType {
WATCH9(100, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_watch9),
ROIDMI(110, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi),
ROIDMI3(112, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi3),
+ CASIOGB6900(120, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_casiogb6900),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
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 10962047a..82c16790c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
@@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900.CasioGB6900DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support;
@@ -171,6 +172,9 @@ public class DeviceSupportFactory {
case ROIDMI3:
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
+ case CASIOGB6900:
+ deviceSupport = new ServiceDeviceSupport(new CasioGB6900DeviceSupport(), 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/casiogb6900/CasioGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTServer.java
new file mode 100644
index 000000000..d76402b8a
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTServer.java
@@ -0,0 +1,163 @@
+/* Copyright (C) 2018 Andreas Böhler
+ based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattServer;
+import android.bluetooth.BluetoothGattServerCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.CountDownLatch;
+
+import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
+import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
+
+class CasioGATTServer extends BluetoothGattServerCallback {
+ private static final Logger LOG = LoggerFactory.getLogger(CasioGATTServer.class);
+
+ private Context mContext;
+ private BluetoothGattServer mBluetoothGattServer;
+ private CasioGB6900DeviceSupport mDeviceSupport = null;
+ private final GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl();
+
+ CasioGATTServer(Context context, CasioGB6900DeviceSupport deviceSupport) {
+ mContext = context;
+ mDeviceSupport = deviceSupport;
+ }
+
+ public void setContext(Context ctx)
+ {
+ mContext = ctx;
+ }
+
+ boolean initialize() {
+ BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+ if (bluetoothManager == null) {
+ return false;
+ }
+ mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this);
+ if (mBluetoothGattServer == null) {
+ return false;
+ }
+
+ BluetoothGattService casioGATTService = new BluetoothGattService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
+ BluetoothGattCharacteristic bluetoothgGATTCharacteristic = new BluetoothGattCharacteristic(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, BluetoothGattCharacteristic.PERMISSION_WRITE);
+ bluetoothgGATTCharacteristic.setValue(new byte[0]);
+
+ BluetoothGattCharacteristic bluetoothgGATTCharacteristic2 = new BluetoothGattCharacteristic(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
+ bluetoothgGATTCharacteristic2.setValue(CasioGB6900Constants.MUSIC_MESSAGE.getBytes());
+
+ BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(CasioGB6900Constants.CCC_DESCRIPTOR_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
+ bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+
+ bluetoothgGATTCharacteristic2.addDescriptor(bluetoothGattDescriptor);
+
+ casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic);
+ casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic2);
+ mBluetoothGattServer.addService(casioGATTService);
+
+ return true;
+ }
+
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
+
+ if (!characteristic.getUuid().equals(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID)) {
+ LOG.warn("unexpected read request");
+ return;
+ }
+
+ LOG.info("will send response to read request from device: " + device.getAddress());
+
+ if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, CasioGB6900Constants.MUSIC_MESSAGE.getBytes())) {
+ LOG.warn("error sending response");
+ }
+ }
+
+
+ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic,
+ boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
+
+ if (!characteristic.getUuid().equals(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID)) {
+ LOG.warn("unexpected write request");
+ return;
+ }
+ if((value[0] & 0x03) == 0)
+ {
+ int button = value[1] & 0x0f;
+ LOG.info("Button pressed: " + button);
+ switch(button)
+ {
+ case 2:
+ musicCmd.event = GBDeviceEventMusicControl.Event.PREVIOUS;
+ break;
+ case 1:
+ musicCmd.event = GBDeviceEventMusicControl.Event.PLAYPAUSE;
+ break;
+ case 0:
+ musicCmd.event = GBDeviceEventMusicControl.Event.NEXT;
+ break;
+ }
+ mDeviceSupport.evaluateGBDeviceEvent(musicCmd);
+ }
+ else
+ {
+ LOG.info("received from device: " + value.toString());
+ }
+ }
+
+ public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
+
+ LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState);
+ if (newState == BluetoothGattServer.STATE_DISCONNECTED) {
+
+ }
+ }
+
+ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor,
+ boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
+
+ LOG.info("onDescriptorWriteRequest() notifications enabled = " + (value[0] == 1));
+ if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, value)) {
+ LOG.warn("onDescriptorWriteRequest() error sending response!");
+ }
+ }
+
+
+ public void onServiceAdded(int status, BluetoothGattService service) {
+ LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid());
+ }
+
+ public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) {
+ LOG.info("onNotificationSent() status = " + status + " to device " + bluetoothDevice.getAddress());
+ }
+
+ void close() {
+ if (mBluetoothGattServer != null) {
+ mBluetoothGattServer.clearServices();
+ mBluetoothGattServer.close();
+ }
+ }
+
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTThread.java
new file mode 100644
index 000000000..6ed5426ca
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTThread.java
@@ -0,0 +1,48 @@
+/* Copyright (C) 2018 Andreas Böhler
+ based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
+import android.content.Context;
+
+public class CasioGATTThread extends Thread {
+ CasioGATTServer mServer = null;
+
+ public CasioGATTThread(Context context, CasioGB6900DeviceSupport deviceSupport)
+ {
+ mServer = new CasioGATTServer(context, deviceSupport);
+ }
+
+ public void setContext(Context ctx)
+ {
+ mServer.setContext(ctx);
+ }
+
+ @Override
+ public void run()
+ {
+ mServer.initialize();
+ while(true)
+ {
+ try {
+ wait(100);
+ } catch(Exception e)
+ {
+
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java
new file mode 100644
index 000000000..2f050a766
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java
@@ -0,0 +1,484 @@
+/* Copyright (C) 2018 Andreas Böhler
+ based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.content.Context;
+import android.net.Uri;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
+import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
+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.model.WeatherSpec;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
+import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
+
+public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceSupport.class);
+
+ public BluetoothGattCharacteristic mCasioCharact1 = null;
+ public BluetoothGattCharacteristic mCasioCharact2 = null;
+ public BluetoothGattCharacteristic mCasioCharact3 = null;
+ public BluetoothGattCharacteristic mCasioCharact4 = null;
+ public BluetoothGattCharacteristic mCasioCharact5 = null;
+ private CasioGATTThread mThread = null;
+
+ public CasioGB6900DeviceSupport() {
+ super(LOG);
+ addSupportedService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE);
+ addSupportedService(CasioGB6900Constants.ALERT_SERVICE_UUID);
+ addSupportedService(CasioGB6900Constants.CASIO_IMMEDIATE_ALERT_SERVICE_UUID);
+ addSupportedService(CasioGB6900Constants.CURRENT_TIME_SERVICE_UUID);
+ addSupportedService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID);
+ addSupportedService(CasioGB6900Constants.WATCH_FEATURES_SERVICE_UUID);
+ addSupportedService(CasioGB6900Constants.CASIO_PHONE_ALERT_STATUS_SERVICE);
+ mThread = new CasioGATTThread(getContext(), this);
+ }
+
+ @Override
+ public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) {
+ super.setContext(gbDevice, btAdapter, context);
+ mThread.setContext(context);
+ mThread.start();
+ }
+
+ @Override
+ protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
+ LOG.info("Initializing");
+
+ gbDevice.setState(GBDevice.State.INITIALIZING);
+ gbDevice.sendDeviceUpdateIntent(getContext());
+
+ mCasioCharact1 = getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT);
+ mCasioCharact2 = getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT);
+ mCasioCharact3 = getCharacteristic(CasioGB6900Constants.FUNCTION_SWITCH_CHARACTERISTIC);
+ mCasioCharact4 = getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID);
+ mCasioCharact5 = getCharacteristic(CasioGB6900Constants.RINGER_CONTROL_POINT);
+
+ builder.setGattCallback(this);
+ builder.notify(mCasioCharact1, true);
+ builder.notify(mCasioCharact2, true);
+ builder.notify(mCasioCharact3, true);
+ builder.notify(mCasioCharact4, true);
+ builder.notify(mCasioCharact5, true);
+
+ LOG.info("Initialization Done");
+
+ return builder;
+ }
+
+ private void writeCasioCurrentTime(TransactionBuilder builder)
+ {
+ byte[] arr = new byte[10];
+ Calendar cal = Calendar.getInstance();
+
+ int year = cal.get(Calendar.YEAR);
+ arr[0] = (byte)((year >>> 0) & 0xff);
+ arr[1] = (byte)((year >>> 8) & 0xff);
+ arr[2] = (byte)(1 + cal.get(Calendar.MONTH));
+ arr[3] = (byte)cal.get(Calendar.DAY_OF_MONTH);
+ arr[4] = (byte)cal.get(Calendar.HOUR_OF_DAY);
+ arr[5] = (byte)cal.get(Calendar.MINUTE);
+ arr[6] = (byte)(1 + cal.get(Calendar.SECOND));
+ byte dayOfWk = (byte)(cal.get(Calendar.DAY_OF_WEEK) - 1);
+ if(dayOfWk == 0)
+ dayOfWk = 7;
+ arr[7] = dayOfWk;
+ arr[8] = (byte)(int) TimeUnit.MILLISECONDS.toSeconds(256 * cal.get(Calendar.MILLISECOND));
+ arr[9] = 1; // or 0?
+
+ BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.CURRENT_TIME_CHARACTERISTIC_UUID);
+ if(charact != null) {
+ charact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
+ builder.write(charact, arr);
+ }
+ else {
+ LOG.warn("Characteristic not found: CURRENT_TIME_CHARACTERISTIC_UUID");
+ }
+ }
+
+ private void writeCasioLocalTimeInformation(TransactionBuilder builder)
+ {
+ Calendar cal = Calendar.getInstance();
+ int zoneOffset = (int)TimeUnit.MILLISECONDS.toMinutes(cal.get(Calendar.ZONE_OFFSET));
+ int dstOffset = (int)TimeUnit.MILLISECONDS.toMinutes(cal.get(Calendar.DST_OFFSET));
+ byte byte0 = (byte)(zoneOffset / 15);
+ byte byte1 = (byte)(dstOffset / 15);
+ BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.LOCAL_TIME_CHARACTERISTIC_UUID);
+ if(charact != null) {
+ builder.write(charact, new byte[]{byte0, byte1});
+ }
+ else {
+ LOG.warn("Characteristic not found: LOCAL_TIME_CHARACTERISTIC_UUID");
+ }
+
+ }
+
+ private void writeCasioVirtualServerFeature(TransactionBuilder builder)
+ {
+ byte byte0 = (byte)0;
+ byte0 |= 1; // Casio Current Time Service
+ byte0 |= 2; // Casio Alert Notification Service
+ byte0 |= 4; // Casio Phone Alert Status Service
+ byte0 |= 8; // Casio Immediate Alert Service
+
+ BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_FEATURES);
+ if(charact != null) {
+ builder.write(charact, new byte[]{byte0, 0x00});
+ }
+ else {
+ LOG.warn("Characteristic not found: CASIO_VIRTUAL_SERVER_FEATURES");
+ }
+ }
+
+ private boolean handleCasioCom(byte[] data)
+ {
+ boolean handled = false;
+ switch(data[0]) // ServiceID - actually an int
+ {
+ case 0:
+ switch(data[2])
+ {
+ case (byte) 1:
+ LOG.info("Initialization done, setting state to INITIALIZED");
+ gbDevice.setState(GBDevice.State.INITIALIZED);
+ gbDevice.sendDeviceUpdateIntent(getContext());
+ break;
+ }
+ break;
+ case 2:
+ switch(data[2]) // Request Type
+ {
+ case (byte) 1:
+ try
+ {
+ TransactionBuilder builder = createTransactionBuilder("writeCasioCurrentTime");
+ writeCasioCurrentTime(builder);
+ performImmediately(builder);
+ handled = true;
+ } catch (IOException e) {
+ LOG.warn(e.getMessage());
+ }
+ break;
+ case (byte) 2:
+ try
+ {
+ TransactionBuilder builder = createTransactionBuilder("writeCasioLocalTimeInformation");
+ writeCasioLocalTimeInformation(builder);
+ performImmediately(builder);
+ handled = true;
+ } catch (IOException e) {
+ LOG.warn(e.getMessage());
+ }
+ break;
+ }
+ break;
+ case 7:
+ try
+ {
+ TransactionBuilder builder = createTransactionBuilder("writeCasioVirtualServerFeature");
+ writeCasioVirtualServerFeature(builder);
+ performImmediately(builder);
+ handled = true;
+ } catch (IOException e) {
+ LOG.warn(e.getMessage());
+ }
+ break;
+ }
+ return handled;
+ }
+
+ @Override
+ public boolean onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ boolean handled = false;
+
+ if (super.onCharacteristicChanged(gatt, characteristic)) {
+ return true;
+ }
+
+ UUID characteristicUUID = characteristic.getUuid();
+ byte[] data = characteristic.getValue();
+ if (data.length == 0)
+ return true;
+
+ if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT))
+ {
+ handled = handleCasioCom(data);
+ }
+
+ if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT))
+ {
+ handled = handleCasioCom(data);
+ }
+
+ if(characteristicUUID.equals(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID))
+ {
+ GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
+ if(data[0] == 0x02) {
+ findPhoneEvent.event = GBDeviceEventFindPhone.Event.START;
+ }
+ else
+ {
+ findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
+ }
+ evaluateGBDeviceEvent(findPhoneEvent);
+ handled = true;
+ }
+
+ if(characteristicUUID.equals(CasioGB6900Constants.RINGER_CONTROL_POINT))
+ {
+ if(data[0] == 0x02)
+ {
+ LOG.info("Mute/ignore call event not yet supported by GB");
+ }
+ handled = true;
+ }
+
+ if(!handled)
+ {
+ LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
+ }
+ return true;
+ }
+
+ private void showNotification(byte icon, String title, String message) {
+ try {
+ TransactionBuilder builder = performInitialized("showNotification");
+ int len;
+
+ byte[] titleBytes = title.getBytes(StandardCharsets.US_ASCII);
+ len = titleBytes.length > 18 ? 18 : titleBytes.length;
+ byte[] msg = new byte[2 + len];
+ msg[0] = icon;
+ msg[1] = 1;
+ for(int i=0; i alarms) {
+
+ }
+
+ @Override
+ public void onSetTime() {
+ try {
+ TransactionBuilder builder = performInitialized("SetTime");
+ writeCasioLocalTimeInformation(builder);
+ writeCasioCurrentTime(builder);
+ performConnected(builder.getTransaction());
+ } catch(IOException e) {
+ LOG.warn(e.getMessage());
+ }
+ }
+
+ @Override
+ public void onSetCallState(CallSpec callSpec) {
+ switch (callSpec.command) {
+ case CallSpec.CALL_INCOMING:
+ showNotification(CasioGB6900Constants.CALL_NOTIFICATION_ID, callSpec.name, callSpec.number);
+ break;
+ }
+ }
+
+ @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, Integer id) {
+
+ }
+
+ @Override
+ public void onAppReorder(UUID[] uuids) {
+
+ }
+
+ @Override
+ public void onFetchRecordedData(int dataTypes) {
+
+ }
+
+ @Override
+ public void onReset(int flags) {
+ try {
+
+ } catch(Exception e) {
+ LOG.warn(e.getMessage());
+ }
+ }
+
+ @Override
+ public void onHeartRateTest() {
+
+ }
+
+ @Override
+ public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
+
+ }
+
+ @Override
+ public void onFindDevice(boolean start) {
+ if(start) {
+ showNotification(CasioGB6900Constants.SNS_NOTIFICATION_ID, "You found it!", "");
+ }
+ }
+
+ @Override
+ public void onSetConstantVibration(int integer) {
+
+ }
+
+ @Override
+ public void onScreenshotReq() {
+
+ }
+
+ @Override
+ public void onEnableHeartRateSleepSupport(boolean enable) {
+
+ }
+
+ @Override
+ public void onSetHeartRateMeasurementInterval(int seconds) {
+
+ }
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+
+ }
+
+ @Override
+ public void onSendConfiguration(String config) {
+
+ }
+
+ @Override
+ public void onTestNewFunction() {
+
+ }
+
+ @Override
+ public void onSendWeather(WeatherSpec weatherSpec) {
+
+ }
+}
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 2f483af39..4ecbb6ed4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
@@ -41,6 +41,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.casiogb6900.CasioGB6900DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator;
@@ -216,6 +217,7 @@ public class DeviceHelper {
result.add(new Watch9DeviceCoordinator());
result.add(new Roidmi1Coordinator());
result.add(new Roidmi3Coordinator());
+ result.add(new CasioGB6900DeviceCoordinator());
return result;
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7731a390f..c2185e8e0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -628,6 +628,7 @@
Watch 9
Roidmi
Roidmi 3
+ Casio GB-6900
Choose export location
Gadgetbridge notifications