1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-02 03:16:07 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/CasioGBX100DeviceSupport.java
andyboeh 02b85f4c11 Casio GBX-100: Add step count data and more device settings
Co-Authored-By: andyboeh <andyboeh@noreply.codeberg.org>
Co-Committed-By: andyboeh <andyboeh@noreply.codeberg.org>
2020-12-20 00:22:35 +01:00

774 lines
31 KiB
Java

/* Copyright (C) 2018-2020 Andreas Böhler, Sebastian Kranz
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casio;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
import nodomain.freeyourgadget.gadgetbridge.devices.casio.CasioConstants;
import nodomain.freeyourgadget.gadgetbridge.devices.casio.CasioGBX100SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants;
import nodomain.freeyourgadget.gadgetbridge.entities.CasioGBX100ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
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.service.devices.casio.operations.FetchStepCountDataOperation;
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 static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOLIGHT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_MESSAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FAKE_RING_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FIND_PHONE_ENABLED;
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_TIMEFORMAT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
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 boolean mFirstConnect = false;
private boolean mGetConfigurationPending = false;
private boolean mRingNotificationPending = false;
private final ArrayList<Integer> mSyncedNotificationIDs = new ArrayList<>();
private int mLastCallId = new AtomicInteger((int) (System.currentTimeMillis()/1000)).incrementAndGet();
private int mFakeRingDurationCounter = 0;
private final Handler mFindPhoneHandler = new Handler();
private final Handler mFakeRingDurationHandler = new Handler();
private final Handler mAutoRemoveMessageHandler = new Handler();
public CasioGBX100DeviceSupport() {
super(LOG);
addSupportedService(CasioConstants.WATCH_FEATURES_SERVICE_UUID);
}
@Override
public boolean connectFirstTime() {
mFirstConnect = true;
return connect();
}
public void setInitialized() {
mFirstConnect = false;
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
try {
new InitOperationGBX100(this, builder, mFirstConnect).perform();
} catch (IOException e) {
GB.toast(getContext(), "Initializing Casio watch failed", Toast.LENGTH_SHORT, GB.ERROR, e);
}
getDevice().setFirmwareVersion("N/A");
getDevice().setFirmwareVersion2("N/A");
//preferences.registerOnSharedPreferenceChangeListener(this);
SharedPreferences prefs = GBApplication.getPrefs().getPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
if(mFirstConnect) {
SharedPreferences preferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress());
SharedPreferences.Editor editor = preferences.edit();
editor.putString("charts_tabs", "activity,activitylist,stepsweek");
editor.apply();
}
return builder;
}
@Override
public boolean onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if(data.length == 0)
return true;
return super.onCharacteristicRead(gatt, characteristic, status);
}
public CasioGBX100ActivitySample getSumWithinRange(int timestamp_from, int timestamp_to) {
int steps = 0;
int calories = 0;
try (DBHandler dbHandler = GBApplication.acquireDB()) {
User user = DBHelper.getUser(dbHandler.getDaoSession());
Device device = DBHelper.getDevice(this.getDevice(), dbHandler.getDaoSession());
CasioGBX100SampleProvider provider = new CasioGBX100SampleProvider(this.getDevice(), dbHandler.getDaoSession());
List<CasioGBX100ActivitySample> samples = provider.getActivitySamples(timestamp_from, timestamp_to);
for(CasioGBX100ActivitySample sample : samples) {
if(sample.getDevice().equals(device) &&
sample.getUser().equals(user)) {
steps += sample.getSteps();
calories += sample.getCalories();
}
}
} catch (Exception e) {
LOG.error("Error fetching activity data.");
}
CasioGBX100ActivitySample ret = new CasioGBX100ActivitySample();
ret.setCalories(calories);
ret.setSteps(steps);
LOG.debug("Fetched for today: " + calories + " cals and " + steps + " steps.");
return ret;
}
private void addGBActivitySamples(ArrayList<CasioGBX100ActivitySample> samples) {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
User user = DBHelper.getUser(dbHandler.getDaoSession());
Device device = DBHelper.getDevice(this.getDevice(), dbHandler.getDaoSession());
CasioGBX100SampleProvider provider = new CasioGBX100SampleProvider(this.getDevice(), dbHandler.getDaoSession());
for (CasioGBX100ActivitySample sample : samples) {
sample.setDevice(device);
sample.setUser(user);
sample.setProvider(provider);
provider.addGBActivitySample(sample);
}
} catch (Exception ex) {
// Why is this a toast? The user doesn't care about the error.
GB.toast(getContext(), "Error saving samples: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
GB.updateTransferNotification(null, "Data transfer failed", false, 0, getContext());
LOG.error(ex.getMessage());
}
}
public void stepCountDataFetched(int totalCount, int totalCalories, ArrayList<CasioGBX100ActivitySample> data) {
LOG.info("Got the following step count data: ");
LOG.info("Total Count: " + totalCount);
LOG.info("Total Calories: " + totalCalories);
addGBActivitySamples(data);
}
@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("ALERT_LEVEL")) {
if(data[1] == 0x02) {
onReverseFindDevice(true);
} else {
onReverseFindDevice(false);
}
return true;
} else if(data[0] == CasioConstants.characteristicToByte.get("CASIO_CURRENT_TIME_MANAGER")) {
if(data[1] == 0x00) {
try {
TransactionBuilder builder = performInitialized("writeCurrentTime");
writeCurrentTime(builder);
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("writing current time failed: " + e.getMessage());
}
}
}
}
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
return super.onCharacteristicChanged(gatt, characteristic);
}
@Override
public boolean useAutoConnect() {
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)
titleBytes = title.getBytes(StandardCharsets.UTF_8);
byte[] messageBytes = new byte[0];
if(message != null)
messageBytes = message.getBytes(StandardCharsets.UTF_8);
byte[] senderBytes = new byte[0];
if(sender != null)
senderBytes = sender.getBytes(StandardCharsets.UTF_8);
byte[] arr = new byte[22];
arr[0] = (byte)(id & 0xff);
arr[1] = (byte) ((id >> 8) & 0xff);
arr[2] = (byte) ((id >> 16) & 0xff);
arr[3] = (byte) ((id >> 24) & 0xff);
arr[4] = delete ? (byte) 0x02 : (byte) 0x00;
arr[5] = (byte) 0x01; // Set to 0x00 to not vibrate/ring for this notification
arr[6] = icon;
// These bytes contain a timestamp, not yet decoded / implemented
// ASCII Codes:
/*arr[7] = (byte) 0x32; // 2
arr[8] = (byte) 0x30; // 0
arr[9] = (byte) 0x32; // 2
arr[10] = (byte) 0x30; // 0
arr[11] = (byte) 0x31; // 1
arr[12] = (byte) 0x31; // 1
arr[13] = (byte) 0x31; // 1
arr[14] = (byte) 0x33; // 3
arr[15] = (byte) 0x54; // T
arr[16] = (byte) 0x30; // 0
arr[17] = (byte) 0x39; // 9
arr[18] = (byte) 0x33; // 3
arr[19] = (byte) 0x31; // 1
arr[20] = (byte) 0x35; // 5
arr[21] = (byte) 0x33;*/// 3
byte[] copy = Arrays.copyOf(arr, arr.length + 2);
copy[copy.length-2] = 0;
copy[copy.length-1] = 0;
if(senderBytes.length > 0) {
copy = Arrays.copyOf(copy, copy.length + senderBytes.length);
copy[copy.length-2-senderBytes.length] = (byte)(senderBytes.length & 0xff);
copy[copy.length-1-senderBytes.length] = (byte)((senderBytes.length >> 8) & 0xff);
System.arraycopy(senderBytes, 0, copy, copy.length - senderBytes.length, senderBytes.length);
}
copy = Arrays.copyOf(copy, copy.length + 2);
copy[copy.length-2] = 0;
copy[copy.length-1] = 0;
if(titleBytes.length > 0) {
copy = Arrays.copyOf(copy, copy.length + titleBytes.length);
copy[copy.length-2-titleBytes.length] = (byte)(titleBytes.length & 0xff);
copy[copy.length-1-titleBytes.length] = (byte)((titleBytes.length >> 8) & 0xff);
System.arraycopy(titleBytes, 0, copy, copy.length - titleBytes.length, titleBytes.length);
}
copy = Arrays.copyOf(copy, copy.length + 2);
copy[copy.length-2] = 0;
copy[copy.length-1] = 0;
//subtitle is currently not supported
copy = Arrays.copyOf(copy, copy.length + 2);
copy[copy.length-2] = 0;
copy[copy.length-1] = 0;
if(messageBytes.length > 0) {
copy = Arrays.copyOf(copy, copy.length + messageBytes.length);
copy[copy.length-2-messageBytes.length] = (byte)(messageBytes.length & 0xff);
copy[copy.length-1-messageBytes.length] = (byte)((messageBytes.length >> 8) & 0xff);
System.arraycopy(messageBytes, 0, copy, copy.length - messageBytes.length, messageBytes.length);
}
for(int i=0; i<copy.length; i++) {
copy[i] = (byte)(~copy[i]);
}
try {
TransactionBuilder builder = performInitialized("showNotification");
builder.write(getCharacteristic(CasioConstants.CASIO_NOTIFICATION_CHARACTERISTIC_UUID), copy);
LOG.info("Showing notification, title: " + title + " message (not sent): " + message);
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("showNotification failed: " + e.getMessage());
}
}
@Override
public void onNotification(final NotificationSpec notificationSpec) {
byte icon;
boolean autoremove = false;
switch (notificationSpec.type.getGenericType()) {
case "generic_calendar":
icon = CasioConstants.CATEGORY_SCHEDULE_AND_ALARM;
break;
case "generic_email":
icon = CasioConstants.CATEGORY_EMAIL;
break;
case "generic_sms":
icon = CasioConstants.CATEGORY_SNS;
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
autoremove = sharedPreferences.getBoolean(PREF_AUTOREMOVE_MESSAGE, false);
break;
default:
icon = CasioConstants.CATEGORY_SNS;
break;
}
LOG.info("onNotification id=" + notificationSpec.getId());
showNotification(icon, notificationSpec.sender, notificationSpec.title, notificationSpec.body, notificationSpec.getId(), false);
mSyncedNotificationIDs.add(notificationSpec.getId());
if(autoremove) {
mAutoRemoveMessageHandler.postDelayed(new Runnable() {
@Override
public void run() {
onDeleteNotification(notificationSpec.getId());
}
}, CasioConstants.CASIO_AUTOREMOVE_MESSAGE_DELAY);
}
// The watch only holds up to 10 notifications. However, the user might have deleted
// some notifications in the meantime, so to be sure, we keep the last 100 IDs.
if(mSyncedNotificationIDs.size() > 100) {
mSyncedNotificationIDs.remove(0);
}
}
@Override
public void onDeleteNotification(int id) {
LOG.info("onDeleteNofication id=" + id);
Integer idInt = id;
if(mSyncedNotificationIDs.contains(idInt)) {
showNotification(CasioConstants.CATEGORY_OTHER, null, null, null, id, true);
mSyncedNotificationIDs.remove(idInt);
}
}
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.parseInt(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();
int year = cal.get(Calendar.YEAR);
arr[0] = CasioConstants.characteristicToByte.get("CASIO_CURRENT_TIME");
arr[1] = (byte)(year & 0xff);
arr[2] = (byte)((year >>> 8) & 0xff);
arr[3] = (byte)(1 + cal.get(Calendar.MONTH));
arr[4] = (byte)cal.get(Calendar.DAY_OF_MONTH);
arr[5] = (byte)cal.get(Calendar.HOUR_OF_DAY);
arr[6] = (byte)cal.get(Calendar.MINUTE);
arr[7] = (byte)(1 + cal.get(Calendar.SECOND));
byte dayOfWk = (byte)(cal.get(Calendar.DAY_OF_WEEK) - 1);
if(dayOfWk == 0)
dayOfWk = 7;
arr[8] = dayOfWk;
arr[9] = (byte)(int) TimeUnit.MILLISECONDS.toSeconds(256 * cal.get(Calendar.MILLISECOND));
arr[10] = 1; // or 0?
writeAllFeatures(builder, arr);
}
public void writeAllFeatures(TransactionBuilder builder, byte[] arr) {
builder.write(getCharacteristic(CasioConstants.CASIO_ALL_FEATURES_CHARACTERISTIC_UUID), arr);
}
public void writeAllFeaturesRequest(TransactionBuilder builder, byte[] arr) {
builder.write(getCharacteristic(CasioConstants.CASIO_READ_REQUEST_FOR_ALL_FEATURES_CHARACTERISTIC_UUID), arr);
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
int alarmOffset = 4;
byte[] data1 = new byte[5];
byte[] data2 = new byte[17];
if(!isConnected())
return;
data1[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_ALM");
data2[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_ALM2");
for(int i=0; i<alarms.size(); i++)
{
byte[] settings = new byte[4];
Alarm alm = alarms.get(i);
if(alm.getEnabled()) {
settings[0] = 0x40;
} else {
settings[0] = 0;
}
if(alm.getRepetition(Alarm.ALARM_ONCE)) {
settings[i * alarmOffset] |= 0x20;
}
settings[1] = 0x40;
settings[2] = (byte)alm.getHour();
settings[3] = (byte)alm.getMinute();
if(i == 0) {
System.arraycopy(settings, 0, data1, 1, settings.length);
} else {
System.arraycopy(settings, 0, data2, 1 + (i-1)*4, settings.length);
}
}
try {
TransactionBuilder builder = performInitialized("setAlarm");
writeAllFeatures(builder, data1);
writeAllFeatures(builder, data2);
builder.queue(getQueue());
} catch(IOException e) {
LOG.error("Error setting alarm: " + e.getMessage());
}
}
@Override
public void onSetTime() {
LOG.debug("onSetTime called");
try {
TransactionBuilder builder = performInitialized("onSetTime");
writeCurrentTime(builder);
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("onSetTime failed: " + e.getMessage());
}
}
@Override
public void onSetCallState(final CallSpec callSpec) {
switch (callSpec.command) {
case CallSpec.CALL_INCOMING:
showNotification(CasioConstants.CATEGORY_INCOMING_CALL, "Phone", callSpec.name, callSpec.number, mLastCallId, false);
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
boolean fakeRingDuration = sharedPreferences.getBoolean(PREF_FAKE_RING_DURATION, false);
if(fakeRingDuration && mFakeRingDurationCounter < CasioConstants.CASIO_FAKE_RING_RETRIES) {
mFakeRingDurationCounter++;
mFakeRingDurationHandler.postDelayed(new Runnable() {
@Override
public void run() {
showNotification(CasioConstants.CATEGORY_INCOMING_CALL, null, null, null, mLastCallId, true);
onSetCallState(callSpec);
}
}, CasioConstants.CASIO_FAKE_RING_SLEEP_DURATION);
} else {
mFakeRingDurationCounter = 0;
}
mRingNotificationPending = true;
break;
default:
if(mRingNotificationPending) {
mFakeRingDurationHandler.removeCallbacksAndMessages(null);
mFakeRingDurationCounter = 0;
showNotification(CasioConstants.CATEGORY_INCOMING_CALL, null, null, null, mLastCallId, true);
mLastCallId = new AtomicInteger((int) (System.currentTimeMillis() / 1000)).incrementAndGet();
}
}
}
@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) {
try {
new FetchStepCountDataOperation(this).perform();
} catch(IOException e) {
GB.toast(getContext(), "Error fetching data", Toast.LENGTH_SHORT, GB.ERROR, e);
}
}
@Override
public void onReset(int flags) {
}
@Override
public void onHeartRateTest() {
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
}
@Override
public void onFindDevice(boolean start) {
}
@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) {
LOG.info("onSendConfiguration" + config);
onSharedPreferenceChanged(null, 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
public void onTestNewFunction() {
byte[] data = new byte[2];
data[0] = (byte)0x2e;
data[1] = (byte)0x03;
try {
TransactionBuilder builder = performInitialized("onTestNewFunction");
writeAllFeaturesRequest(builder, data);
builder.queue(getQueue());
} catch(IOException e) {
LOG.error("Error setting alarm: " + e.getMessage());
}
}
@Override
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 {
switch (key) {
case DeviceSettingsPreferenceConst.PREF_WEARLOCATION:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_WRIST).perform();
break;
case PREF_USER_STEPS_GOAL:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_STEP_GOAL).perform();
break;
case PREF_USER_ACTIVETIME_MINUTES:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_ACTIVITY_GOAL).perform();
break;
case PREF_USER_DISTANCE_METERS:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_DISTANCE_GOAL).perform();
break;
case PREF_USER_GENDER:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_GENDER).perform();
break;
case PREF_USER_HEIGHT_CM:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_HEIGHT).perform();
break;
case PREF_USER_WEIGHT_KG:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_WEIGHT).perform();
break;
case PREF_USER_YEAR_OF_BIRTH:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_BIRTHDAY).perform();
break;
case PREF_TIMEFORMAT:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_TIMEFORMAT).perform();
break;
case PREF_KEY_VIBRATION:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_KEY_VIBRATION).perform();
break;
case PREF_AUTOLIGHT:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_AUTOLIGHT).perform();
break;
case PREF_OPERATING_SOUNDS:
new SetConfigurationOperation(this, CasioConstants.ConfigurationOption.OPTION_OPERATING_SOUNDS).perform();
break;
case PREF_FAKE_RING_DURATION:
case PREF_FIND_PHONE_ENABLED:
case MakibesHR3Constants.PREF_FIND_PHONE_DURATION:
// No action, we check the shared preferences when the device tries to ring the phone.
break;
default:
}
} catch (IOException e) {
LOG.info("Error sending configuration change to watch");
}
}
}