mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-27 11:05:49 +01:00
Add support for synchronizing profile settings
This commit is contained in:
parent
2ef9128cf8
commit
8fd0e1f13f
@ -120,6 +120,18 @@ public final class CasioConstants {
|
||||
MODEL_CASIO_GBX100
|
||||
}
|
||||
|
||||
public enum ConfigurationOption {
|
||||
OPTION_GENDER,
|
||||
OPTION_WEIGHT,
|
||||
OPTION_HEIGHT,
|
||||
OPTION_WRIST,
|
||||
OPTION_BIRTHDAY,
|
||||
OPTION_STEP_GOAL,
|
||||
OPTION_DISTANCE_GOAL,
|
||||
OPTION_ACTIVITY_GOAL,
|
||||
OPTION_ALL
|
||||
}
|
||||
|
||||
public static Map<String, Byte> characteristicToByte = new HashMap<String, Byte>() {
|
||||
{
|
||||
put("CASIO_WATCH_NAME", (byte) 0x23);
|
||||
@ -135,6 +147,9 @@ public final class CasioConstants {
|
||||
put("CASIO_DST_SETTING", (byte) 0x1e);
|
||||
put("CASIO_SERVICE_DISCOVERY_MANAGER", (byte) 0x47);
|
||||
put("CASIO_CURRENT_TIME", (byte) 0x09);
|
||||
put("CASIO_SETTING_FOR_USER_PROFILE", (byte) 0x45);
|
||||
put("CASIO_SETTING_FOR_TARGET_VALUE", (byte) 0x43);
|
||||
put("ALERT_LEVEL", (byte) 0x0a);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public class CasioGBX100DeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount() {
|
||||
return 0; // 4 regular and one snooze
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -149,4 +149,12 @@ public class CasioGBX100DeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
|
||||
return new int[]{
|
||||
R.xml.devicesettings_find_phone,
|
||||
R.xml.devicesettings_wearlocation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,11 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.casio;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -37,10 +36,12 @@ import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.casio.CasioConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
@ -51,24 +52,29 @@ 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.GattCharacteristic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.ServerTransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.operations.GetConfigurationOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.operations.InitOperationGBX100;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.operations.SetConfigurationOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FIND_PHONE_ENABLED;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_ACTIVETIME_MINUTES;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_DISTANCE_METERS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_STEPS_GOAL;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_WEIGHT_KG;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_YEAR_OF_BIRTH;
|
||||
|
||||
public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CasioGBX100DeviceSupport.class);
|
||||
|
||||
private final ArrayList<BluetoothGattCharacteristic> mCasioCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
|
||||
private MusicSpec mBufferMusicSpec = null;
|
||||
private MusicStateSpec mBufferMusicStateSpec = null;
|
||||
private BluetoothGatt mBtGatt = null;
|
||||
private boolean mFirstConnect = false;
|
||||
private boolean mGetConfigurationPending = false;
|
||||
private ArrayList<Integer> mSyncedNotificationIDs = new ArrayList<>();
|
||||
private int mLastCallId = 0;
|
||||
private final Handler mFindPhoneHandler = new Handler();
|
||||
|
||||
public CasioGBX100DeviceSupport() {
|
||||
super(LOG);
|
||||
@ -82,15 +88,10 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
return connect();
|
||||
}
|
||||
|
||||
public void setInitialized(TransactionBuilder builder) {
|
||||
public void setInitialized() {
|
||||
mFirstConnect = false;
|
||||
builder.add(new SetDeviceStateAction(gbDevice, GBDevice.State.INITIALIZED, getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt) {
|
||||
mBtGatt = gatt;
|
||||
super.onServicesDiscovered(gatt);
|
||||
gbDevice.setState(GBDevice.State.INITIALIZED);
|
||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -105,6 +106,12 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
getDevice().setFirmwareVersion("N/A");
|
||||
getDevice().setFirmwareVersion2("N/A");
|
||||
|
||||
SharedPreferences preferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress());
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
SharedPreferences prefs = GBApplication.getPrefs().getPreferences();
|
||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -124,13 +131,22 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic) {
|
||||
boolean handled = false;
|
||||
|
||||
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] == CasioConstants.characteristicToByte.get("ALERT_LEVEL")) {
|
||||
if(data[1] == 0x02) {
|
||||
onReverseFindDevice(true);
|
||||
} else {
|
||||
onReverseFindDevice(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
|
||||
return super.onCharacteristicChanged(gatt, characteristic);
|
||||
}
|
||||
@ -140,6 +156,14 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void syncProfile() {
|
||||
try {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_ALL).perform();
|
||||
} catch (IOException e) {
|
||||
GB.toast(getContext(), "Sending Casio configuration failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotification(byte icon, String sender, String title, String message, int id, boolean delete) {
|
||||
byte[] titleBytes = new byte[0];
|
||||
if(title != null)
|
||||
@ -256,6 +280,51 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void onReverseFindDevice(boolean start) {
|
||||
if (start) {
|
||||
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
||||
|
||||
String findPhone = sharedPreferences.getString(PREF_FIND_PHONE_ENABLED, getContext().getString(R.string.p_off));
|
||||
|
||||
if(findPhone.equals(getContext().getString(R.string.p_off)))
|
||||
return;
|
||||
|
||||
GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.START;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
|
||||
if(!findPhone.equals(getContext().getString(R.string.p_on))) {
|
||||
String duration = sharedPreferences.getString(MakibesHR3Constants.PREF_FIND_PHONE_DURATION, "0");
|
||||
|
||||
try {
|
||||
int iDuration;
|
||||
|
||||
try {
|
||||
iDuration = Integer.valueOf(duration);
|
||||
} catch (Exception ex) {
|
||||
LOG.warn(ex.getMessage());
|
||||
iDuration = 60;
|
||||
}
|
||||
if(iDuration > 0) {
|
||||
this.mFindPhoneHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onReverseFindDevice(false);
|
||||
}
|
||||
}, iDuration * 1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Unexpected exception in MiBand2Coordinator.getTime: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Always send stop, ignore preferences.
|
||||
GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeCurrentTime(TransactionBuilder builder) {
|
||||
byte[] arr = new byte[11];
|
||||
Calendar cal = Calendar.getInstance();
|
||||
@ -306,11 +375,14 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
final AtomicInteger c = new AtomicInteger((int) (System.currentTimeMillis()/1000));
|
||||
switch (callSpec.command) {
|
||||
case CallSpec.CALL_INCOMING:
|
||||
showNotification(CasioConstants.CATEGORY_INCOMING_CALL, "Phone", callSpec.name, callSpec.number, c.incrementAndGet(), false);
|
||||
final AtomicInteger c = new AtomicInteger((int) (System.currentTimeMillis()/1000));
|
||||
mLastCallId = c.incrementAndGet();
|
||||
showNotification(CasioConstants.CATEGORY_INCOMING_CALL, "Phone", callSpec.name, callSpec.number, mLastCallId, false);
|
||||
break;
|
||||
case CallSpec.CALL_END:
|
||||
showNotification(CasioConstants.CATEGORY_INCOMING_CALL, null, null, null, mLastCallId, true);
|
||||
default:
|
||||
LOG.info("not sending CallSpec since only CALL_INCOMING is handled");
|
||||
break;
|
||||
@ -423,12 +495,26 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(String config) {
|
||||
LOG.info("onSendConfiguration" + config);
|
||||
}
|
||||
|
||||
public void onGetConfigurationFinished() {
|
||||
mGetConfigurationPending = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadConfiguration(String config) {
|
||||
|
||||
LOG.info("onReadConfiguration" + config);
|
||||
// This is called upon pairing to retrieve the current watch settings, if any
|
||||
if(config == null) {
|
||||
try {
|
||||
mGetConfigurationPending = true;
|
||||
new GetConfigurationOperation(this, true).perform();
|
||||
} catch (IOException e) {
|
||||
mGetConfigurationPending = false;
|
||||
GB.toast(getContext(), "Reading Casio configuration failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -440,4 +526,44 @@ public class CasioGBX100DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
public void onSendWeather(WeatherSpec weatherSpec) {
|
||||
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
LOG.debug(key + " changed");
|
||||
|
||||
if (!this.isConnected()) {
|
||||
LOG.debug("ignoring change, we're disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
if(mGetConfigurationPending) {
|
||||
LOG.debug("Preferences are being fetched right now");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (key.equals(DeviceSettingsPreferenceConst.PREF_WEARLOCATION)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_WRIST).perform();
|
||||
} else if(key.equals(PREF_USER_STEPS_GOAL)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_STEP_GOAL).perform();
|
||||
} else if(key.equals(PREF_USER_ACTIVETIME_MINUTES)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_ACTIVITY_GOAL).perform();
|
||||
} else if(key.equals(PREF_USER_DISTANCE_METERS)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_DISTANCE_GOAL).perform();
|
||||
} else if(key.equals(PREF_USER_GENDER)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_GENDER).perform();
|
||||
} else if(key.equals(PREF_USER_HEIGHT_CM)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_HEIGHT).perform();
|
||||
} else if(key.equals(PREF_USER_WEIGHT_KG)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_WEIGHT).perform();
|
||||
} else if(key.equals(PREF_USER_YEAR_OF_BIRTH)) {
|
||||
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_BIRTHDAY).perform();
|
||||
} else if (key.equals(PREF_FIND_PHONE_ENABLED) ||
|
||||
key.equals(MakibesHR3Constants.PREF_FIND_PHONE_DURATION)) {
|
||||
// No action, we check the shared preferences when the device tries to ring the phone.
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.info("Error sending configuration change to watch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,119 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.casio.operations;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsFragment;
|
||||
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.btle.actions.PlainAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGBX100DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.BcdUtil;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
|
||||
|
||||
public class GetConfigurationOperation extends AbstractBTLEOperation<CasioGBX100DeviceSupport> {
|
||||
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 doPerform() throws IOException {
|
||||
byte[] command = new byte[1];
|
||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE");
|
||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||
builder.setGattCallback(this);
|
||||
support.writeAllFeaturesRequest(builder, command);
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void operationFinished() {
|
||||
operationStatus = OperationStatus.FINISHED;
|
||||
if (getDevice() != null) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("finishe operation");
|
||||
builder.wait(0);
|
||||
builder.setGattCallback(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();
|
||||
}
|
||||
|
||||
@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] == CasioConstants.characteristicToByte.get("CASIO_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]) - 1;
|
||||
|
||||
// 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();
|
||||
|
||||
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;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
@ -260,16 +260,6 @@ public class InitOperationGBX100 extends AbstractBTLEOperation<CasioGBX100Device
|
||||
}
|
||||
}
|
||||
|
||||
private void setInitialized() {
|
||||
try {
|
||||
TransactionBuilder builder = createTransactionBuilder("setInitialized");
|
||||
support.setInitialized(builder);
|
||||
support.performImmediately(builder);
|
||||
} catch(IOException e) {
|
||||
LOG.error("Error setting device to initialized: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void enableAllFeatures(TransactionBuilder builder, boolean enable) {
|
||||
builder.notify(getCharacteristic(CasioConstants.CASIO_ALL_FEATURES_CHARACTERISTIC_UUID), enable);
|
||||
}
|
||||
@ -365,12 +355,18 @@ public class InitOperationGBX100 extends AbstractBTLEOperation<CasioGBX100Device
|
||||
if(mFirstConnect)
|
||||
writeAllFeaturesInit();
|
||||
else
|
||||
setInitialized();
|
||||
support.setInitialized();
|
||||
}
|
||||
} else if(data[0] == 0x3d) {
|
||||
LOG.info("Init operation done.");
|
||||
// Finally, we set the state to initialized here!
|
||||
setInitialized();
|
||||
support.setInitialized();
|
||||
if(mFirstConnect) {
|
||||
support.onReadConfiguration(null);
|
||||
} else {
|
||||
// on first connect, this is called by onReadConfiguration
|
||||
support.syncProfile();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
@ -0,0 +1,228 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.casio.operations;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.casio.CasioConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGBX100DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.BcdUtil;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.GENDER_MALE;
|
||||
|
||||
public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX100DeviceSupport> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetConfigurationOperation.class);
|
||||
private final CasioGBX100DeviceSupport support;
|
||||
private final CasioConstants.ConfigurationOption option;
|
||||
|
||||
public SetConfigurationOperation(CasioGBX100DeviceSupport support, CasioConstants.ConfigurationOption option) {
|
||||
super(support);
|
||||
this.support = support;
|
||||
this.option = option;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPerform() throws IOException {
|
||||
byte[] command = new byte[1];
|
||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE");
|
||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||
builder.setGattCallback(this);
|
||||
support.writeAllFeaturesRequest(builder, command);
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@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)) {
|
||||
byte[] oldData = new byte[data.length];
|
||||
System.arraycopy(data, 0, oldData, 0, data.length);
|
||||
|
||||
if (data[0] == CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE")) {
|
||||
|
||||
ActivityUser user = new ActivityUser();
|
||||
boolean all = (option == CasioConstants.ConfigurationOption.OPTION_ALL);
|
||||
if (option == CasioConstants.ConfigurationOption.OPTION_GENDER || all) {
|
||||
if (user.getGender() == GENDER_MALE) {
|
||||
data[1] = (byte) (data[1] & ~0x01);
|
||||
} else {
|
||||
data[1] = (byte) (data[1] | 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=2; i<data.length; i++) {
|
||||
data[i] = (byte)~data[i];
|
||||
}
|
||||
|
||||
if (option == CasioConstants.ConfigurationOption.OPTION_HEIGHT || all) {
|
||||
int height = user.getHeightCm();
|
||||
data[2] = BcdUtil.toBcd8(height % 100);
|
||||
data[3] = BcdUtil.toBcd8((height - (height % 100)) / 100);
|
||||
}
|
||||
if (option == CasioConstants.ConfigurationOption.OPTION_WEIGHT || all) {
|
||||
int weight = user.getWeightKg();
|
||||
data[4] = BcdUtil.toBcd8(weight % 100);
|
||||
data[5] = BcdUtil.toBcd8((weight - (weight % 100)) / 100);
|
||||
}
|
||||
if (option == CasioConstants.ConfigurationOption.OPTION_WRIST || all) {
|
||||
String location = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).getString(PREF_WEARLOCATION, "left");
|
||||
if (location == "right") {
|
||||
data[1] = (byte) (data[1] | 0x02);
|
||||
} else {
|
||||
data[1] = (byte) (data[1] & ~0x02);
|
||||
}
|
||||
}
|
||||
if(option == CasioConstants.ConfigurationOption.OPTION_BIRTHDAY || all) {
|
||||
int year = user.getYearOfBirth();
|
||||
// Month and Day are not configured in Gadgetbridge!
|
||||
int month = 1;
|
||||
int day = 1;
|
||||
data[6] = BcdUtil.toBcd8(year % 100);
|
||||
data[7] = BcdUtil.toBcd8((year - (year % 100)) / 100);
|
||||
data[8] = BcdUtil.toBcd8(month);
|
||||
data[9] = BcdUtil.toBcd8(day + 1);
|
||||
}
|
||||
|
||||
for(int i=2; i<data.length; i++) {
|
||||
data[i] = (byte)~data[i];
|
||||
}
|
||||
|
||||
if(Arrays.equals(oldData, data)) {
|
||||
LOG.info("No configuration update required");
|
||||
requestTargetSettings();
|
||||
} else {
|
||||
// Target settings will be requested in write callback
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("setConfiguration");
|
||||
builder.setGattCallback(this);
|
||||
support.writeAllFeatures(builder, data);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
LOG.info("Error writing configuration to Casio watch");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (data[0] == CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_TARGET_VALUE")) {
|
||||
ActivityUser user = new ActivityUser();
|
||||
boolean all = (option == CasioConstants.ConfigurationOption.OPTION_ALL);
|
||||
|
||||
if(option == CasioConstants.ConfigurationOption.OPTION_STEP_GOAL || all) {
|
||||
int steps = user.getStepsGoal();
|
||||
data[1] = (byte)(steps & 0xff);
|
||||
data[2] = (byte)((steps >> 8) & 0xff);
|
||||
}
|
||||
|
||||
if(option == CasioConstants.ConfigurationOption.OPTION_DISTANCE_GOAL || all) {
|
||||
// The watch requires a monthly goal, so we multiply that with 30
|
||||
// and divide it by 100 because the value is set in 100m units
|
||||
int distance = user.getDistanceMeters() * 30;
|
||||
distance = distance / 100;
|
||||
data[6] = (byte)(distance & 0xff);
|
||||
data[7] = (byte)((distance >> 8) & 0xff);
|
||||
}
|
||||
|
||||
if(option == CasioConstants.ConfigurationOption.OPTION_ACTIVITY_GOAL || all) {
|
||||
// The watch requires a monthly goal, so we multiply that with 30
|
||||
int time = user.getActiveTimeMinutes() * 30;
|
||||
data[9] = (byte)(time & 0xff);
|
||||
data[10] = (byte)((time >> 8) & 0xff);
|
||||
}
|
||||
|
||||
if(Arrays.equals(oldData, data)) {
|
||||
LOG.info("No configuration update required");
|
||||
operationFinished();
|
||||
} else {
|
||||
// Operation will be finished in Gatt callback
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("setConfiguration");
|
||||
builder.setGattCallback(this);
|
||||
support.writeAllFeatures(builder, data);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
LOG.info("Error writing configuration to Casio watch");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
|
||||
return super.onCharacteristicChanged(gatt, characteristic);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void operationFinished() {
|
||||
LOG.info("SetConfigurationOperation finished");
|
||||
|
||||
operationStatus = OperationStatus.FINISHED;
|
||||
if (getDevice() != null) {
|
||||
unsetBusy();
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("finishe operation");
|
||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
||||
builder.wait(0);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.info("Error resetting Gatt callback: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requestTargetSettings() {
|
||||
byte[] command = new byte[1];
|
||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_TARGET_VALUE");
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||
builder.setGattCallback(this);
|
||||
support.writeAllFeaturesRequest(builder, command);
|
||||
builder.queue(getQueue());
|
||||
} catch(IOException e) {
|
||||
LOG.info("Error requesting Casio configuration");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
|
||||
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] == CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE")) {
|
||||
requestTargetSettings();
|
||||
return true;
|
||||
}
|
||||
if(data[0] == CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_TARGET_VALUE")) {
|
||||
operationFinished();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onCharacteristicWrite(gatt, characteristic, status);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user