/* Copyright (C) 2023-2024 Johannes Krude 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.casio.gbx100; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.SharedPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.casio.CasioConstants; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.Casio2C2DSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.gbx100.CasioGBX100DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus; import nodomain.freeyourgadget.gadgetbridge.util.BcdUtil; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOLIGHT; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_KEY_VIBRATION; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_OPERATING_SOUNDS; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION; public class GetConfigurationOperation extends AbstractBTLEOperation { private static final Logger LOG = LoggerFactory.getLogger(GetConfigurationOperation.class); private final CasioGBX100DeviceSupport support; private final boolean mFirstConnect; public GetConfigurationOperation(CasioGBX100DeviceSupport support, boolean firstconnect) { super(support); this.support = support; this.mFirstConnect = firstconnect; } @Override protected void prePerform() throws IOException { super.prePerform(); getDevice().setBusyTask("GetConfigurationOperation starting..."); // mark as busy quickly to avoid interruptions from the outside } @Override protected void doPerform() throws IOException { byte[] command = new byte[1]; command[0] = Casio2C2DSupport.FEATURE_SETTING_FOR_USER_PROFILE; TransactionBuilder builder = performInitialized("getConfiguration"); builder.setCallback(this); support.writeAllFeaturesRequest(builder, command); builder.queue(getQueue()); } @Override protected void operationFinished() { operationStatus = OperationStatus.FINISHED; unsetBusy(); if (getDevice() != null) { try { TransactionBuilder builder = performInitialized("finished operation"); builder.wait(0); builder.setCallback(null); // unset ourselves from being the queue's gatt callback builder.queue(getQueue()); } catch (IOException ex) { LOG.info("Error resetting Gatt callback: " + ex.getMessage()); } } support.onGetConfigurationFinished(); } private void requestBasicSettings() { byte[] command = new byte[1]; command[0] = Casio2C2DSupport.FEATURE_SETTING_FOR_BASIC; try { TransactionBuilder builder = performInitialized("getConfiguration"); builder.setCallback(this); support.writeAllFeaturesRequest(builder, command); builder.queue(getQueue()); } catch(IOException e) { LOG.info("Error requesting Casio configuration"); } } @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { UUID characteristicUUID = characteristic.getUuid(); byte[] data = characteristic.getValue(); if (data.length == 0) return true; if (characteristicUUID.equals(CasioConstants.CASIO_ALL_FEATURES_CHARACTERISTIC_UUID)) { if (data[0] == Casio2C2DSupport.FEATURE_SETTING_FOR_USER_PROFILE) { boolean female = ((data[1] & 0x01) == 0x01); boolean right = ((data[1] & 0x02) == 0x02); byte[] compData = new byte[data.length]; for (int i = 0; i < data.length; i++) { compData[i] = (byte) (~data[i]); } int height = BcdUtil.fromBcd8(compData[2]) + BcdUtil.fromBcd8(compData[3]) * 100; int weight = BcdUtil.fromBcd8(compData[4]) + BcdUtil.fromBcd8(compData[5]) * 100; int year = BcdUtil.fromBcd8(compData[6]) + BcdUtil.fromBcd8(compData[7]) * 100; int month = BcdUtil.fromBcd8(compData[8]); int day = BcdUtil.fromBcd8(compData[9]); // Store only the device-specific settings on first-connect SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences.Editor editor = prefs.edit(); editor.putString(PREF_WEARLOCATION, right ? "right" : "left"); editor.apply(); requestBasicSettings(); return true; } else if (data[0] == Casio2C2DSupport.FEATURE_SETTING_FOR_BASIC) { boolean timeformat = ((data[1] & 0x01) == 0x01); boolean autolight = ((data[1] & 0x04) == 0x00); boolean key_vibration = (data[10] == 0x01); boolean operating_sounds = ((data[1] & 0x02) == 0x00); // Store only the device-specific settings on first-connect SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(PREF_AUTOLIGHT, autolight); editor.putBoolean(PREF_KEY_VIBRATION, key_vibration); editor.putBoolean(PREF_OPERATING_SOUNDS, operating_sounds); editor.apply(); LOG.info("GetConfigurationOperation finished"); operationFinished(); // Retrieve all settings from the watch, this overwrites the profile // on first connect, overwrite the watch settings if (!mFirstConnect) { } else { support.syncProfile(); } return true; } } LOG.info("Unhandled characteristic changed: " + characteristicUUID); return super.onCharacteristicChanged(gatt, characteristic); } @Override public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { return super.onCharacteristicRead(gatt, characteristic, status); } }