From 8b7d8530973679267dddaa5a12d6e8f2d01365d6 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 3 Oct 2019 11:52:46 +0200 Subject: [PATCH 01/34] merged. added makibes hr3 OnSharedPreferenceChangeListener. added makibes hr3 reverse find device (find phone). added makibes hr3 heart rate/steps/firmware version. --- .../gadgetbridge/daogen/GBDaoGenerator.java | 15 +- app/src/main/AndroidManifest.xml | 1 + .../makibeshr3/MakibesHR3Constants.java | 112 +++++--- .../makibeshr3/MakibesHR3Coordinator.java | 28 +- .../makibeshr3/MakibesHR3SampleProvider.java | 84 ++++++ .../makibeshr3/MakibesHR3DeviceSupport.java | 244 +++++++++++++++++- 6 files changed, 439 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3SampleProvider.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 3a55d6e58..13868d787 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -45,7 +45,7 @@ public class GBDaoGenerator { public static void main(String[] args) throws Exception { - Schema schema = new Schema(20, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(21, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -60,6 +60,7 @@ public class GBDaoGenerator { Entity tag = addTag(schema); Entity userDefinedActivityOverlay = addActivityDescription(schema, tag, user); + addMakibesHR3ActivitySample(schema, user, device); addMiBandActivitySample(schema, user, device); addPebbleHealthActivitySample(schema, user, device); addPebbleHealthActivityKindOverlay(schema, user, device); @@ -186,6 +187,16 @@ public class GBDaoGenerator { return deviceAttributes; } + private static Entity addMakibesHR3ActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "MakibesHR3ActivitySample"); + activitySample.implementsSerializable(); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + return activitySample; + } + private static Entity addMiBandActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "MiBandActivitySample"); activitySample.implementsSerializable(); @@ -363,7 +374,7 @@ public class GBDaoGenerator { alarm.addBooleanProperty("smartWakeup").notNull(); alarm.addIntProperty("repetition").notNull().codeBeforeGetter( "public boolean isRepetitive() { return getRepetition() != ALARM_ONCE; } " + - "public boolean getRepetition(int dow) { return (this.repetition & dow) > 0; }" + "public boolean getRepetition(int dow) { return (this.repetition & dow) > 0; }" ); alarm.addIntProperty("hour").notNull(); alarm.addIntProperty("minute").notNull(); diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8aecd746f..eb7b6d8de 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,6 +21,7 @@ + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 7805d8ac7..9424959b9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -24,15 +24,7 @@ public final class MakibesHR3Constants { public static final UUID UUID_SERVICE = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); - - // time - // mode ab:00:04:ff:7c:80:** (00: 24h, 01: 12h) - - // confirm write? - // ab:00:09:ff:52:80:00:13:06:09:0f:0b - - // disconnect? - // ab:00:03:ff:ff:80 + public static final UUID UUID_CHARACTERISTIC_REPORT = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); // Services and Characteristics // 00001801-0000-1000-8000-00805f9b34fb @@ -44,8 +36,8 @@ public final class MakibesHR3Constants { // 00002a04-0000-1000-8000-00805f9b34fb // 00002aa6-0000-1000-8000-00805f9b34fb // 6e400001-b5a3-f393-e0a9-e50e24dcca9e // Nordic UART Service - // 6e400002-b5a3-f393-e0a9-e50e24dcca9e // control - // 6e400003-b5a3-f393-e0a9-e50e24dcca9e + // 6e400002-b5a3-f393-e0a9-e50e24dcca9e // control (RX) + // 6e400003-b5a3-f393-e0a9-e50e24dcca9e // report // 0000fee7-0000-1000-8000-00805f9b34fb // 0000fec9-0000-1000-8000-00805f9b34fb // 0000fea1-0000-1000-8000-00805f9b34fb @@ -55,10 +47,8 @@ public final class MakibesHR3Constants { // ab 00 [argument_count] ff [command] 80 [arguments] // where [argument_count] is [arguments].length + 3 - // refresh sends - // 51 - // 52 - // 93 (CMD_SET_DATE_TIME) + // Report structure is the same but 80 might by different + public static final byte[] DATA_TEMPLATE = { (byte) 0xab, @@ -75,36 +65,78 @@ public final class MakibesHR3Constants { public static final int DATA_ARGUMENTS_INDEX = 6; + // This is also used with different parameters. + // steps take up more bytes. I don't know which ones yet. + // Only sent after we send CMD_51 + // 00 (maybe also used for steps) + // [steps hi] + // [steps lo] + // 00 + // 00 + // 01 (also was 0b. Maybe minutes of activity.) + // 00 + // 00 + // 00 + // 00 + // 00 + public static final byte RPRT_FITNESS = (byte) 0x51; + + + // enable (00/01) + public static final byte RPRT_REVERSE_FIND_DEVICE = (byte) 0x7d; + + + // heart rate + public static final byte RPRT_HEARTRATE = (byte) 0x84; + + + // 2 arguments. + public static final byte RPRT_91 = (byte) 0x91; + + // firmware_major + // firmware_minor + // 37 + // 00 + // 00 + // 00 + // 00 + // 00 + // 00 + // 20 + // 0e + public static final byte RPRT_SOFTWARE = (byte) 0x92; + // 00 public static final byte CMD_FACTORY_RESET = (byte) 0x23; // 00 // year (+2000) - // month - // day - // 0b - // 00 + // month (not current! but close) + // day (not current! but close) + // 0b (A) + // 00 (B) // year (+2000) - // month - // day - // 0b - // 19 - public static final byte CMD_UNKNOWN_51 = (byte) 0x51; + // month (not current! but close) + // day (not current! but close) + // 0b (this is >= (A)) + // 19 (this is >= (B)) + public static final byte CMD_REQUEST_FITNESS = (byte) 0x51; // this is the last command sent on sync // 00 // year (+2000) - // month + // month (not current!) // 14 this isn't the current day // hour (current) // minute (current) - public static final byte CMD_UNKNOWN_52 = (byte) 0x52; + public static final byte CMD_52 = (byte) 0x52; public static final byte CMD_FIND_DEVICE = (byte) 0x71; + // WearFit writes uses other sources as well. They don't do anything though. public static final byte ARG_SEND_NOTIFICATION_SOURCE_CALL = (byte) 0x01; public static final byte ARG_SEND_NOTIFICATION_SOURCE_STOP_CALL = (byte) 0x02; public static final byte ARG_SEND_NOTIFICATION_SOURCE_MESSAGE = (byte) 0x03; @@ -118,7 +150,7 @@ public final class MakibesHR3Constants { public static final byte ARG_SEND_NOTIFICATION_SOURCE_WEIBO = (byte) 0x13; public static final byte ARG_SEND_NOTIFICATION_SOURCE_KAKOTALK = (byte) 0x14; // ARG_SET_NOTIFICATION_SOURCE_* - // 02 + // 02 (This is 00 and 01 during connection. I don't know what it does. Maybe clears notifications?) // ASCII public static final byte CMD_SEND_NOTIFICATION = (byte) 0x72; @@ -177,6 +209,10 @@ public final class MakibesHR3Constants { public static final byte CMD_SET_HEADS_UP_SCREEN = (byte) 0x77; + // Looks like enable/disable. + public static final byte CMD_78 = (byte) 0x78; + + // The watch enters photograph mode, but doesn't appear to send a trigger signal. // enable (00/01) public static final byte CMD_SET_PHOTOGRAPH_MODE = (byte) 0x79; @@ -188,14 +224,24 @@ public final class MakibesHR3Constants { // 7b has 1 argument. Looks like enable/disable. - // 7e has 14 arguments. - public static final byte ARG_SET_TIMEMODE_24H = 0x00; public static final byte ARG_SET_TIMEMODE_12H = 0x01; // ARG_SET_TIMEMODE_* public static final byte CMD_SET_TIMEMODE = (byte) 0x7c; + // 5 arguments. + public static final byte CMD_7f = (byte) 0x7f; + + + // enable (00/01) + public static final byte CMD_SET_REAL_TIME_HEART_RATE = (byte) 0x84; + + + // looks like enable/disable. + public static final byte CMD_85 = (byte) 0x85; + + // 00 // year hi // year lo @@ -207,6 +253,14 @@ public final class MakibesHR3Constants { public static final byte CMD_SET_DATE_TIME = (byte) 0x93; + // looks like enable/disable. + public static final byte CMD_96 = (byte) 0x96; + + + // looks like enable/disable. + public static final byte CMD_e5 = (byte) 0xe5; + + // If this is sent after {@link CMD_FACTORY_RESET}, it's a shutdown, not a reboot. // Rebooting resets the watch face and wallpaper. public static final byte CMD_REBOOT = (byte) 0xff; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index a81c789a9..e867d1a2d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -16,10 +16,18 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3; +/* + * @author Alejandro Ladera Chamorro <11555126+tiparega@users.noreply.github.com> + */ + + import android.app.Activity; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattCharacteristic; import android.content.Context; -import android.content.SharedPreferences; import android.net.Uri; +import android.util.Log; +import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -48,20 +56,24 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3Coordinator.class); - public static byte getTimeMode(String deviceAddress) { - SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); - + public static byte getTimeMode(SharedPreferences sharedPrefs) { String tmode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); LOG.debug("tmode is " + tmode); - if (getContext().getString(R.string.p_timeformat_24h).equals(tmode)) { + if (tmode.equals(getContext().getString(R.string.p_timeformat_24h))) { return MakibesHR3Constants.ARG_SET_TIMEMODE_24H; } else { return MakibesHR3Constants.ARG_SET_TIMEMODE_12H; } } + public static byte getTimeMode(String deviceAddress) { + SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); + + return getTimeMode(sharedPrefs); + } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { @@ -92,7 +104,7 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { @Override public boolean supportsRealtimeData() { - return false; + return true; } @Override @@ -123,12 +135,12 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { @Override public boolean supportsActivityTracking() { - return false; + return true; } @Override public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { - return null; + return new MakibesHR3SampleProvider(device, session); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3SampleProvider.java new file mode 100644 index 000000000..fd6f4ee01 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3SampleProvider.java @@ -0,0 +1,84 @@ +/* Copyright (C) 2018-2019 Daniele Gobbetti, Sebastian Kranz + + 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.makibeshr3; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public class MakibesHR3SampleProvider extends AbstractSampleProvider { + + private GBDevice mDevice; + private DaoSession mSession; + + public MakibesHR3SampleProvider(GBDevice device, DaoSession session) { + super(device, session); + + mSession = session; + mDevice = device; + } + + @Override + public int normalizeType(int rawType) { + return rawType; + } + + @Override + public int toRawActivityKind(int activityKind) { + return activityKind; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity; + } + + @Override + public MakibesHR3ActivitySample createActivitySample() { + return new MakibesHR3ActivitySample(); + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getMakibesHR3ActivitySampleDao(); + } + + @Nullable + @Override + protected Property getRawKindSampleProperty() { + return MakibesHR3ActivitySampleDao.Properties.RawKind; + } + + @NonNull + @Override + protected Property getTimestampSampleProperty() { + return MakibesHR3ActivitySampleDao.Properties.Timestamp; + } + + @NonNull + @Override + protected Property getDeviceIdentifierSampleProperty() { + return MakibesHR3ActivitySampleDao.Properties.DeviceId; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 6a782481e..246019739 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,7 +1,26 @@ +// TODO: WearFit resets today's step count when it's used after GB. + +// TODO: Battery level + +// TODO: ALARM REMINDER REPETITION + +// TODO: It'd be cool if we could change the language. There's no official way to do so, but the +// TODO: watch is sold as chinese/english. + package nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3; +import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; +import android.os.Build; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.widget.Toast; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,13 +29,23 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.User; 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.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -24,12 +53,20 @@ 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.serial.GBDeviceProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT; +import static nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants.RPRT_SOFTWARE; + +public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implements SharedPreferences.OnSharedPreferenceChangeListener { private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3DeviceSupport.class); - private BluetoothGattCharacteristic ctrlCharacteristic = null; + private Vibrator mVibrator; + + public BluetoothGattCharacteristic ctrlCharacteristic = null; + public BluetoothGattCharacteristic rprtCharacteristic = null; + public MakibesHR3DeviceSupport() { super(LOG); @@ -42,6 +79,17 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { return false; } + public MakibesHR3ActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { + MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp(timestampInSeconds); + sample.setProvider(provider); + + return sample; + } + + @Override public void onNotification(NotificationSpec notificationSpec) { TransactionBuilder transactionBuilder = this.createTransactionBuilder("onnotificaiton"); @@ -233,7 +281,34 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + TransactionBuilder transactionBuilder = this.createTransactionBuilder("finddevice"); + this.setEnableRealTimeHeartRate(transactionBuilder, enable); + + try { + this.performConnected(transactionBuilder.getTransaction()); + } catch (Exception e) { + LOG.debug("ERROR"); + } + } + + private void onReverseFindDevice(boolean start) { + final long[] PATTERN = new long[]{ + 100, 100, + 100, 100, + 100, 100, + 500 + }; + + if (start) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + this.mVibrator.vibrate(VibrationEffect.createWaveform(PATTERN, 0)); + } else { + this.mVibrator.vibrate(PATTERN, 0); + } + } else { + this.mVibrator.cancel(); + } } @Override @@ -304,12 +379,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { } private MakibesHR3DeviceSupport sendUserInfo(TransactionBuilder builder) { - // builder.write(ctrlCharacteristic, MakibesHR3Constants.CMD_SET_PREF_START); - // builder.write(ctrlCharacteristic, MakibesHR3Constants.CMD_SET_PREF_START1); - syncPreferences(builder); - // builder.write(ctrlCharacteristic, new byte[]{MakibesHR3Constants.CMD_SET_CONF_END}); return this; } @@ -334,30 +405,158 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { return this; } + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + LOG.debug(key + " changed"); + TransactionBuilder transactionBuilder = this.createTransactionBuilder("onSharedPreferenceChanged"); + + if (key.equals(PREF_TIMEFORMAT)) { + this.setTimeMode(transactionBuilder); + } else { + return; + } + + try { + this.performConnected(transactionBuilder.getTransaction()); + } catch (Exception ex) { + LOG.warn(ex.getMessage()); + } + } + @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { gbDevice.setState(GBDevice.State.INITIALIZING); gbDevice.sendDeviceUpdateIntent(getContext()); this.ctrlCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_CONTROL); + this.rprtCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT); + this.mVibrator = (Vibrator) this.getContext().getSystemService(Context.VIBRATOR_SERVICE); + + builder.notify(this.rprtCharacteristic, true); builder.setGattCallback(this); + // Allow modifications builder.write(this.ctrlCharacteristic, new byte[]{0x01, 0x00}); // Initialize device sendUserInfo(builder); //Sync preferences + this.requestFitness(builder, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0); + gbDevice.setState(GBDevice.State.INITIALIZED); gbDevice.sendDeviceUpdateIntent(getContext()); getDevice().setFirmwareVersion("N/A"); getDevice().setFirmwareVersion2("N/A"); + SharedPreferences preferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress()); + + // TODO: Why doesn't this work? + preferences.registerOnSharedPreferenceChangeListener(this); + return builder; } + private void broadcastActivity(Integer heartRate, Integer steps) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + + User user = DBHelper.getUser(dbHandler.getDaoSession()); + Device device = DBHelper.getDevice(this.getDevice(), dbHandler.getDaoSession()); + + MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession()); + + int timeStamp = (int) (System.currentTimeMillis() / 1000); + + MakibesHR3ActivitySample sample = this.createActivitySample(device, user, timeStamp, provider); + + if (heartRate != null) { + sample.setHeartRate(heartRate); + } + + if (steps != null) { + sample.setSteps(steps); + } + + sample.setRawKind(-1); + + provider.addGBActivitySample(sample); + + Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) + .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample) + .putExtra(DeviceService.EXTRA_TIMESTAMP, timeStamp); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + + } catch (Exception ex) { + GB.toast(getContext(), "Error saving steps data: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + GB.updateTransferNotification(null, "Data transfer failed", false, 0, getContext()); + } + } + + private void onReceiveFitness(int steps) { + LOG.info("steps: " + steps); + + this.broadcastActivity(null, steps); + } + + private void onReceiveHeartRate(int heartRate) { + LOG.info("heart rate: " + heartRate); + + this.broadcastActivity(heartRate, null); + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + + byte[] data = characteristic.getValue(); + if (data.length < 6) + return true; + + UUID characteristicUuid = characteristic.getUuid(); + + if (characteristicUuid.equals(rprtCharacteristic.getUuid())) { + byte[] value = characteristic.getValue(); + byte[] arguments = new byte[value.length - 6]; + + for (int i = 0; i < arguments.length; ++i) { + arguments[i] = value[i + 6]; + } + + byte report = value[4]; + + LOG.debug("report: " + Integer.toHexString((int) report)); + + switch (report) { + case MakibesHR3Constants.RPRT_FITNESS: + if (value.length == 17) { + this.onReceiveFitness( + (int) arguments[1] * 0xff + arguments[2] + ); + } + break; + case MakibesHR3Constants.RPRT_REVERSE_FIND_DEVICE: + this.onReverseFindDevice(arguments[0] == 0x01); + break; + case MakibesHR3Constants.RPRT_HEARTRATE: + if (value.length == 7) { + this.onReceiveHeartRate((int) arguments[0]); + } + break; + case RPRT_SOFTWARE: + if (arguments.length == 11) { + this.getDevice().setFirmwareVersion(((int) arguments[0]) + "." + ((int) arguments[1])); + } + break; + } + } + + return false; + } + /** * @param command * @param data @@ -427,6 +626,31 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { return this.reboot(transaction); } + private MakibesHR3DeviceSupport requestFitness(TransactionBuilder transaction, + int yearStart, int monthStart, int dayStart, + int a4, int a5, + int yearEnd, int monthEnd, int dayEnd, + int a9, int a10) { + + byte[] data = this.craftData(MakibesHR3Constants.CMD_REQUEST_FITNESS, + new byte[]{ + (byte) (yearStart - 2000), + (byte) monthStart, + (byte) dayStart, + (byte) a4, + (byte) a5, + (byte) (yearEnd - 2000), + (byte) monthEnd, + (byte) dayEnd, + (byte) a9, + (byte) a10 + }); + + transaction.write(this.ctrlCharacteristic, data); + + return this; + } + private MakibesHR3DeviceSupport findDevice(TransactionBuilder transaction) { transaction.write(this.ctrlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FIND_DEVICE)); @@ -475,6 +699,14 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport { return this; } + private MakibesHR3DeviceSupport setEnableRealTimeHeartRate(TransactionBuilder transaction, boolean enable) { + byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_REAL_TIME_HEART_RATE, new byte[]{(byte) (enable ? 0x01 : 0x00)}); + + transaction.write(this.ctrlCharacteristic, data); + + return this; + } + private MakibesHR3DeviceSupport setDateTime(TransactionBuilder transaction, int year, int month, From 81aa66c2d4009c9530570b521e29de14033c6e9c Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 3 Oct 2019 11:53:54 +0200 Subject: [PATCH 02/34] removed todo. fixed typo. --- .../gadgetbridge/activities/charts/LiveActivityFragment.java | 2 +- .../service/devices/makibeshr3/MakibesHR3DeviceSupport.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index c2ab8b94a..bdb095c2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -346,7 +346,7 @@ public class LiveActivityFragment extends AbstractChartFragment { renderCharts(); - // have to enable it again and again to keep it measureing + // have to enable it again and again to keep it measuring GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 246019739..93a27942a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -452,7 +452,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement SharedPreferences preferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress()); - // TODO: Why doesn't this work? preferences.registerOnSharedPreferenceChangeListener(this); return builder; From 0a7639877b11ae7ad4ab69ac4d0168a00a9cd400 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 3 Oct 2019 12:15:19 +0200 Subject: [PATCH 03/34] fixed alarm repetition --- .../makibeshr3/MakibesHR3Constants.java | 11 +++++-- .../makibeshr3/MakibesHR3DeviceSupport.java | 32 +++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 9424959b9..d3e925515 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -156,14 +156,21 @@ public final class MakibesHR3Constants { public static final byte ARG_SET_ALARM_REMINDER_REPEAT_WEEKDAY = (byte) 0x1F; - public static final byte ARG_SET_ALARM_REMINDER_REPEAT_CUSTOM = (byte) 0x40; public static final byte ARG_SET_ALARM_REMINDER_REPEAT_EVERY_DAY = (byte) 0x7F; public static final byte ARG_SET_ALARM_REMINDER_REPEAT_ONE_TIME = (byte) 0x80; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_MONDAY = (byte) 0x01; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_TUESDAY = (byte) 0x02; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_WEDNESDAY = (byte) 0x04; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_THURSDAY = (byte) 0x08; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_FRIDAY = (byte) 0x10; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_SATURDAY = (byte) 0x20; + public static final byte ARG_SET_ALARM_REMINDER_REPEAT_SUNDAY = (byte) 0x40; + // reminder id starting at 0 // enable (00/01) // hour // minute - // ARG_SET_ALARM_REMINDER_REPEAT_* + // bit field of ARG_SET_ALARM_REMINDER_REPEAT_* public static final byte CMD_SET_ALARM_REMINDER = (byte) 0x73; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 93a27942a..fcd8c0bd7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -2,8 +2,6 @@ // TODO: Battery level -// TODO: ALARM REMINDER REPETITION - // TODO: It'd be cool if we could change the language. There's no official way to do so, but the // TODO: watch is sold as chinese/english. @@ -161,6 +159,34 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement for (int i = 0; i < alarms.size(); ++i) { Alarm alarm = alarms.get(i); + byte repetition = 0x00; + + switch (alarm.getRepetition()) { + case Alarm.ALARM_ONCE: + repetition = MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_ONE_TIME; + break; + + case Alarm.ALARM_MON: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_MONDAY; + case Alarm.ALARM_TUE: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_TUESDAY; + case Alarm.ALARM_WED: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_WEDNESDAY; + case Alarm.ALARM_THU: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_THURSDAY; + case Alarm.ALARM_FRI: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_FRIDAY; + case Alarm.ALARM_SAT: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_SATURDAY; + case Alarm.ALARM_SUN: + repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_SUNDAY; + break; + + default: + LOG.warn("invalid alarm repetition " + alarm.getRepetition()); + break; + } + // Should we use @alarm.getPosition() rather than @i? this.setAlarmReminder( transactionBuilder, @@ -168,7 +194,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement alarm.getEnabled(), alarm.getHour(), alarm.getMinute(), - MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_CUSTOM); + repetition); } try { From fe667ffdd8e1648aa1bfbdd274406e62227d0ce1 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 3 Oct 2019 12:19:12 +0200 Subject: [PATCH 04/34] added makibes hr3 todo and heart rate constants --- .../gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java | 5 +++++ .../service/devices/makibeshr3/MakibesHR3DeviceSupport.java | 2 ++ 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index d3e925515..8bf50f86f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -86,6 +86,11 @@ public final class MakibesHR3Constants { public static final byte RPRT_REVERSE_FIND_DEVICE = (byte) 0x7d; + // The proximity sensor sees air.. + public static final byte ARG_HEARTRATE_NO_TARGET = (byte) 0xff; + // The hr sensor didn't find the heart rate yet. + public static final byte ARG_HEARTRATE_NO_READING = (byte) 0x00; + // heart rate public static final byte RPRT_HEARTRATE = (byte) 0x84; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index fcd8c0bd7..0cccbfab0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -2,6 +2,8 @@ // TODO: Battery level +// TODO: Step target + // TODO: It'd be cool if we could change the language. There's no official way to do so, but the // TODO: watch is sold as chinese/english. From 5e581f781add653132878afb2e4747ba71e6759a Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 3 Oct 2019 12:28:20 +0200 Subject: [PATCH 05/34] added todos --- .../service/devices/makibeshr3/MakibesHR3DeviceSupport.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 0cccbfab0..9862e76f2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -2,7 +2,9 @@ // TODO: Battery level -// TODO: Step target +// TODO: Step target and personal information + +// TODO: Read activity history from device // TODO: It'd be cool if we could change the language. There's no official way to do so, but the // TODO: watch is sold as chinese/english. From 40696cb3e69a8b91cea81bd652d3ee4e0e0b302e Mon Sep 17 00:00:00 2001 From: Cre3per Date: Sun, 6 Oct 2019 15:46:15 +0200 Subject: [PATCH 06/34] makibes hr3. added multi-byte command support. now uploading personal data to the watch. --- .../makibeshr3/MakibesHR3Constants.java | 43 +++++- .../makibeshr3/MakibesHR3Coordinator.java | 11 +- .../makibeshr3/MakibesHR3DeviceSupport.java | 140 ++++++++++++------ 3 files changed, 130 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 8bf50f86f..d585d7754 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -46,8 +46,7 @@ public final class MakibesHR3Constants { // Command structure // ab 00 [argument_count] ff [command] 80 [arguments] // where [argument_count] is [arguments].length + 3 - - // Report structure is the same but 80 might by different + // 80 might by different. public static final byte[] DATA_TEMPLATE = { @@ -64,6 +63,14 @@ public final class MakibesHR3Constants { public static final int DATA_COMMAND_INDEX = 4; public static final int DATA_ARGUMENTS_INDEX = 6; + // blood oxygen percentage + public static final byte[] RPRT_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x12 }; + + + // blood oxygen percentage + // blood oxygen percentage + public static final byte[] RPRT_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; + // This is also used with different parameters. // steps take up more bytes. I don't know which ones yet. @@ -95,8 +102,9 @@ public final class MakibesHR3Constants { public static final byte RPRT_HEARTRATE = (byte) 0x84; - // 2 arguments. - public static final byte RPRT_91 = (byte) 0x91; + // charging (00/01) + // battery percentage (step size is 20). + public static final byte RPRT_BATTERY = (byte) 0x91; // firmware_major // firmware_minor @@ -128,6 +136,16 @@ public final class MakibesHR3Constants { // 19 (this is >= (B)) public static final byte CMD_REQUEST_FITNESS = (byte) 0x51; + // I don't think the watch can do this, but it replies. + // enable (00/01) + public static final byte[] CMD_SET_REAL_TIME_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x12 }; + + + // When disabling, the watch replies with RPRT_SINGLE_BLOOD_OXYGEN + // enable (00/01) + public static final byte[] CMD_SET_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; + + // this is the last command sent on sync // 00 // year (+2000) @@ -181,8 +199,7 @@ public final class MakibesHR3Constants { public static final byte ARG_SET_PERSONAL_INFORMATION_UNIT_DISTANCE_MILES = (byte) 0x00; public static final byte ARG_SET_PERSONAL_INFORMATION_UNIT_DISTANCE_KILOMETERS = (byte) 0x01; - public static final byte ARG_SET_PERSONAL_INFORMATION_UNIT_LENGTH_INCHES = (byte) 0x00; - public static final byte ARG_SET_PERSONAL_INFORMATION_UNIT_LENGTH_CENTIMETERS = (byte) 0x01; + // step length (in/cm) // step length (in/cm) // age (years) // height (in/cm) @@ -242,8 +259,16 @@ public final class MakibesHR3Constants { public static final byte CMD_SET_TIMEMODE = (byte) 0x7c; - // 5 arguments. - public static final byte CMD_7f = (byte) 0x7f; + // 14 arguments. Watch might reply with RPRT_BATTERY. + public static final byte CMD_7e = (byte) 0x7e; + + + // 01 + // fall hour + // fall minute + // awake hour + // awake minute + public static final byte CMD_SET_SLEEP_TIME = (byte) 0x7f; // enable (00/01) @@ -264,6 +289,8 @@ public final class MakibesHR3Constants { // second public static final byte CMD_SET_DATE_TIME = (byte) 0x93; + // 3 arguments. Sent when saving personal information. + public static final byte CMD_95 = (byte) 0x95; // looks like enable/disable. public static final byte CMD_96 = (byte) 0x96; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index e867d1a2d..4b00c42fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -16,18 +16,11 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3; -/* - * @author Alejandro Ladera Chamorro <11555126+tiparega@users.noreply.github.com> - */ - import android.app.Activity; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattCharacteristic; import android.content.Context; -import android.net.Uri; -import android.util.Log; import android.content.SharedPreferences; +import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -79,7 +72,7 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); - // TODO: + // TODO: Device discovery if ((name != null) && name.equals("Y808")) { return DeviceType.MAKIBESHR3; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 9862e76f2..ac42f106d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,13 +1,13 @@ -// TODO: WearFit resets today's step count when it's used after GB. - -// TODO: Battery level - -// TODO: Step target and personal information +// TODO: WearFit sometimes resets today's step count when it's used after GB. +// TODO: Where can I view today's steps in GB? +// TODO: GB adds the step count to the week's total every time we broadcast a sample. // TODO: Read activity history from device +// TODO: All the commands that aren't supported by GB should be added to device specific settings. + // TODO: It'd be cool if we could change the language. There's no official way to do so, but the -// TODO: watch is sold as chinese/english. +// TODO: watch is sold as chinese/english. Screen on time would be nice too. package nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3; @@ -32,9 +32,9 @@ import java.util.Calendar; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Coordinator; @@ -43,7 +43,9 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; @@ -58,7 +60,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.GB; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT; -import static nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants.RPRT_SOFTWARE; public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -66,8 +67,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private Vibrator mVibrator; - public BluetoothGattCharacteristic ctrlCharacteristic = null; - public BluetoothGattCharacteristic rprtCharacteristic = null; + private BluetoothGattCharacteristic mControlCharacteristic = null; + private BluetoothGattCharacteristic mReportCharacteristic = null; public MakibesHR3DeviceSupport() { @@ -242,32 +243,26 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement @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 @@ -277,7 +272,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement @Override public void onFetchRecordedData(int dataTypes) { - + // what is this? } @Override @@ -306,7 +301,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement @Override public void onHeartRateTest() { - } @Override @@ -419,14 +413,17 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.setTimeMode(transaction); this.setDateTime(transaction); // setDayOfWeek(transaction); - // setTimeMode(transaction); + this.setTimeMode(transaction); - // setGender(transaction); - // setAge(transaction); - // setWeight(transaction); - // setHeight(transaction); + ActivityUser activityUser = new ActivityUser(); + + this.setPersonalInformation(transaction, + (byte) Math.round(activityUser.getHeightCm() * 0.43), // Thanks no1f1 + activityUser.getAge(), + activityUser.getHeightCm(), + activityUser.getWeightKg(), + activityUser.getStepsGoal() / 1000); - // setGoal(transaction); // setLanguage(transaction); // setScreenTime(transaction); // setUnit(transaction); @@ -457,21 +454,22 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement gbDevice.setState(GBDevice.State.INITIALIZING); gbDevice.sendDeviceUpdateIntent(getContext()); - this.ctrlCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_CONTROL); - this.rprtCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT); + this.mControlCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_CONTROL); + this.mReportCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT); this.mVibrator = (Vibrator) this.getContext().getSystemService(Context.VIBRATOR_SERVICE); - builder.notify(this.rprtCharacteristic, true); + builder.notify(this.mReportCharacteristic, true); builder.setGattCallback(this); // Allow modifications - builder.write(this.ctrlCharacteristic, new byte[]{0x01, 0x00}); + builder.write(this.mControlCharacteristic, new byte[]{0x01, 0x00}); // Initialize device sendUserInfo(builder); //Sync preferences + // TODO: arguments this.requestFitness(builder, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0); gbDevice.setState(GBDevice.State.INITIALIZED); @@ -507,10 +505,12 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement sample.setSteps(steps); } + // I saw this somewhere else and it works. sample.setRawKind(-1); provider.addGBActivitySample(sample); + // TODO: steps aren't real time. Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample) .putExtra(DeviceService.EXTRA_TIMESTAMP, timeStamp); @@ -547,7 +547,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement UUID characteristicUuid = characteristic.getUuid(); - if (characteristicUuid.equals(rprtCharacteristic.getUuid())) { + if (characteristicUuid.equals(mReportCharacteristic.getUuid())) { byte[] value = characteristic.getValue(); byte[] arguments = new byte[value.length - 6]; @@ -575,7 +575,17 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.onReceiveHeartRate((int) arguments[0]); } break; - case RPRT_SOFTWARE: + case MakibesHR3Constants.RPRT_BATTERY: + if (arguments.length == 2) { + GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo(); + + batteryInfo.level = (short) arguments[1]; + batteryInfo.state = ((arguments[0] == 0x01) ? BatteryState.BATTERY_CHARGING : BatteryState.BATTERY_NORMAL); + + this.handleGBDeviceEvent(batteryInfo); + } + break; + case MakibesHR3Constants.RPRT_SOFTWARE: if (arguments.length == 11) { this.getDevice().setFirmwareVersion(((int) arguments[0]) + "." + ((int) arguments[1])); } @@ -586,24 +596,26 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return false; } - /** - * @param command - * @param data - * @return - */ - private byte[] craftData(byte command, byte[] data) { + private byte[] craftData(byte[] command, byte[] data) { byte[] result = new byte[MakibesHR3Constants.DATA_TEMPLATE.length + data.length]; System.arraycopy(MakibesHR3Constants.DATA_TEMPLATE, 0, result, 0, MakibesHR3Constants.DATA_TEMPLATE.length); result[MakibesHR3Constants.DATA_ARGUMENT_COUNT_INDEX] = (byte) (data.length + 3); - result[MakibesHR3Constants.DATA_COMMAND_INDEX] = command; + + for (int i = 0; i < command.length; ++i) { + result[MakibesHR3Constants.DATA_COMMAND_INDEX + i] = command[i]; + } System.arraycopy(data, 0, result, 6, data.length); return result; } + private byte[] craftData(byte command, byte[] data) { + return this.craftData(new byte[]{command}, data); + } + private byte[] craftData(byte command) { return this.craftData(command, new byte[]{}); @@ -650,7 +662,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } private MakibesHR3DeviceSupport factoryReset(TransactionBuilder transaction) { - transaction.write(this.ctrlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FACTORY_RESET)); + transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FACTORY_RESET)); return this.reboot(transaction); } @@ -675,13 +687,13 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement (byte) a10 }); - transaction.write(this.ctrlCharacteristic, data); + transaction.write(this.mControlCharacteristic, data); return this; } private MakibesHR3DeviceSupport findDevice(TransactionBuilder transaction) { - transaction.write(this.ctrlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FIND_DEVICE)); + transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FIND_DEVICE)); return this; } @@ -697,7 +709,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } this.writeSafe( - this.ctrlCharacteristic, + this.mControlCharacteristic, transaction, this.craftData(MakibesHR3Constants.CMD_SEND_NOTIFICATION, data)); @@ -706,7 +718,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private MakibesHR3DeviceSupport setAlarmReminder(TransactionBuilder transaction, int id, boolean enable, int hour, int minute, byte repeat) { - transaction.write(this.ctrlCharacteristic, + transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_SET_ALARM_REMINDER, new byte[]{ (byte) id, (byte) (enable ? 0x01 : 0x00), @@ -718,12 +730,46 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this; } + /** + * @param transactionBuilder + * @param stepLength cm + * @param age years + * @param height cm + * @param weight kg + * @param stepGoal kilo + */ + private MakibesHR3DeviceSupport setPersonalInformation(TransactionBuilder transactionBuilder, + int stepLength, int age, int height, int weight, int stepGoal) { + byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_PERSONAL_INFORMATION, + new byte[]{ + (byte) stepLength, + (byte) age, + (byte) height, + (byte) weight, + MakibesHR3Constants.ARG_SET_PERSONAL_INFORMATION_UNIT_DISTANCE_KILOMETERS, + (byte) stepGoal, + (byte) 0x5a, + (byte) 0x82, + (byte) 0x3c, + (byte) 0x5a, + (byte) 0x28, + (byte) 0xb4, + (byte) 0x5d, + (byte) 0x64, + + }); + + transactionBuilder.write(this.mControlCharacteristic, data); + + return this; + } + private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transaction) { byte value = MakibesHR3Coordinator.getTimeMode(getDevice().getAddress()); byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_TIMEMODE, new byte[]{value}); - transaction.write(this.ctrlCharacteristic, data); + transaction.write(this.mControlCharacteristic, data); return this; } @@ -731,7 +777,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private MakibesHR3DeviceSupport setEnableRealTimeHeartRate(TransactionBuilder transaction, boolean enable) { byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_REAL_TIME_HEART_RATE, new byte[]{(byte) (enable ? 0x01 : 0x00)}); - transaction.write(this.ctrlCharacteristic, data); + transaction.write(this.mControlCharacteristic, data); return this; } @@ -747,7 +793,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_DATE_TIME, new byte[]{ (byte) 0x00, - (byte) (year & 0xff00), + (byte) ((year & 0xff00) >> 8), (byte) (year & 0x00ff), (byte) month, (byte) day, @@ -756,7 +802,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement (byte) second }); - transaction.write(this.ctrlCharacteristic, data); + transaction.write(this.mControlCharacteristic, data); return this; } @@ -776,7 +822,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } private MakibesHR3DeviceSupport reboot(TransactionBuilder transaction) { - transaction.write(this.ctrlCharacteristic, this.craftData(MakibesHR3Constants.CMD_REBOOT)); + transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_REBOOT)); return this; } From c0a6566410033aab46b93f4ed9eeba355b630aae Mon Sep 17 00:00:00 2001 From: Cre3per Date: Sun, 6 Oct 2019 15:47:53 +0200 Subject: [PATCH 07/34] makibes hr3. corrected alarm slot count. --- .../gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index 4b00c42fc..6d1ab96fb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -148,8 +148,7 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { @Override public int getAlarmSlotCount() { - // TODO: - return 5; + return 8; } @Override From da1a72c6c6a0f9ea44058a9fc88f44e83f3d7af9 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Mon, 7 Oct 2019 15:28:54 +0200 Subject: [PATCH 08/34] makibes hr3. implemented deleteDevice. implemented heart rate history download. cleaned up sample handling. --- .../makibeshr3/MakibesHR3Constants.java | 59 +++++--- .../makibeshr3/MakibesHR3Coordinator.java | 13 +- .../makibeshr3/MakibesHR3DeviceSupport.java | 131 +++++++++++++----- 3 files changed, 145 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index d585d7754..1981bef1f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -72,7 +72,6 @@ public final class MakibesHR3Constants { public static final byte[] RPRT_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; - // This is also used with different parameters. // steps take up more bytes. I don't know which ones yet. // Only sent after we send CMD_51 // 00 (maybe also used for steps) @@ -86,7 +85,32 @@ public final class MakibesHR3Constants { // 00 // 00 // 00 - public static final byte RPRT_FITNESS = (byte) 0x51; + public static final byte[] RPRT_FITNESS = new byte[]{ (byte) 0x51, 0x08 }; + + // year (+2000) + // month + // day + // hour + // minute + // heart rate + // heart rate + public static final byte[] RPRT_HEART_RATE_SAMPLE = new byte[]{ (byte) 0x51, (byte) 0x11 }; + + // year + // month + // day + // hour? + // 00 + // 01 + // 39 + // 00 + // 00 + // 0d + // 00 + // 00 + // 00 + // 00 + public static final byte[] RPRT_51_20 = new byte[]{ (byte) 0x51, (byte) 0x20 }; // enable (00/01) @@ -123,34 +147,35 @@ public final class MakibesHR3Constants { public static final byte CMD_FACTORY_RESET = (byte) 0x23; + // enable (00/01) + public static final byte[] CMD_SET_REAL_TIME_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x12 }; + + + // After disabling, the watch replies with RPRT_SINGLE_BLOOD_OXYGEN + // enable (00/01) + public static final byte[] CMD_SET_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; + + + // The times in here are probably 'from' and 'until' // 00 // year (+2000) - // month (not current! but close) - // day (not current! but close) + // month (not current! but close. Multiple of 5) + // day (not current! but close. Multiple of 5) // 0b (A) // 00 (B) // year (+2000) // month (not current! but close) // day (not current! but close) - // 0b (this is >= (A)) - // 19 (this is >= (B)) + // 0b (this is >= (A). Multiple of 5) + // 19 (this is >= (B). Multiple of 5) public static final byte CMD_REQUEST_FITNESS = (byte) 0x51; - // I don't think the watch can do this, but it replies. - // enable (00/01) - public static final byte[] CMD_SET_REAL_TIME_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x12 }; - - // When disabling, the watch replies with RPRT_SINGLE_BLOOD_OXYGEN - // enable (00/01) - public static final byte[] CMD_SET_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; - - - // this is the last command sent on sync + // this looks like a request for the heart rate history // 00 // year (+2000) // month (not current!) - // 14 this isn't the current day + // day (not current!) // hour (current) // minute (current) public static final byte CMD_52 = (byte) 0x52; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index 6d1ab96fb..e5ddbc4a7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -28,6 +28,7 @@ import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; @@ -37,6 +38,8 @@ 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.entities.MakibesHR3ActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -50,11 +53,9 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3Coordinator.class); public static byte getTimeMode(SharedPreferences sharedPrefs) { - String tmode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); + String timeMode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); - LOG.debug("tmode is " + tmode); - - if (tmode.equals(getContext().getString(R.string.p_timeformat_24h))) { + if (timeMode.equals(getContext().getString(R.string.p_timeformat_24h))) { return MakibesHR3Constants.ARG_SET_TIMEMODE_24H; } else { return MakibesHR3Constants.ARG_SET_TIMEMODE_12H; @@ -82,7 +83,9 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - + Long deviceId = device.getId(); + QueryBuilder qb = session.getMakibesHR3ActivitySampleDao().queryBuilder(); + qb.where(MakibesHR3ActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index ac42f106d..d3271f566 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,8 +1,11 @@ // TODO: WearFit sometimes resets today's step count when it's used after GB. // TODO: Where can I view today's steps in GB? -// TODO: GB adds the step count to the week's total every time we broadcast a sample. +// TODO: GB accumulates all step samples, even if they're part of the same day. -// TODO: Read activity history from device +// TODO: Activity history download progress. +// TODO: Remove downloaded history from the device. + +// TODO: Request and handle step history from the device. // TODO: All the commands that aren't supported by GB should be added to device specific settings. @@ -27,8 +30,11 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.sql.Date; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -43,6 +49,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; @@ -485,7 +493,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return builder; } - private void broadcastActivity(Integer heartRate, Integer steps) { + private void addGBActivitySamples(MakibesHR3ActivitySample[] samples) { try (DBHandler dbHandler = GBApplication.acquireDB()) { User user = DBHelper.getUser(dbHandler.getDaoSession()); @@ -493,45 +501,93 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession()); - int timeStamp = (int) (System.currentTimeMillis() / 1000); + for (MakibesHR3ActivitySample sample : samples) { + sample.setDevice(device); + sample.setUser(user); + sample.setProvider(provider); - MakibesHR3ActivitySample sample = this.createActivitySample(device, user, timeStamp, provider); + sample.setRawIntensity(ActivitySample.NOT_MEASURED); - if (heartRate != null) { - sample.setHeartRate(heartRate); + provider.addGBActivitySample(sample); } - if (steps != null) { - sample.setSteps(steps); - } - - // I saw this somewhere else and it works. - sample.setRawKind(-1); - - provider.addGBActivitySample(sample); - - // TODO: steps aren't real time. - Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) - .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample) - .putExtra(DeviceService.EXTRA_TIMESTAMP, timeStamp); - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); - } catch (Exception ex) { - GB.toast(getContext(), "Error saving steps data: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + // 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()); } } + private void addGBActivitySample(MakibesHR3ActivitySample sample) { + this.addGBActivitySamples(new MakibesHR3ActivitySample[]{ sample }); + } + + /** + * Should only be called after the sample has been populated by + * {@link MakibesHR3DeviceSupport#addGBActivitySample} or + * {@link MakibesHR3DeviceSupport#addGBActivitySamples} + * @param sample + */ + private void broadcastSample(MakibesHR3ActivitySample sample) { + Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) + .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample) + .putExtra(DeviceService.EXTRA_TIMESTAMP, sample.getTimestamp()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + private void onReceiveFitness(int steps) { LOG.info("steps: " + steps); - this.broadcastActivity(null, steps); + MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); + + sample.setSteps(steps); + sample.setTimestamp((int) (System.currentTimeMillis() / 1000)); + + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); + this.addGBActivitySample(sample); } private void onReceiveHeartRate(int heartRate) { LOG.info("heart rate: " + heartRate); - this.broadcastActivity(heartRate, null); + MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); + + if (heartRate > 0) { + sample.setHeartRate(heartRate); + sample.setTimestamp((int) (System.currentTimeMillis() / 1000)); + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); + } else { + if (heartRate == MakibesHR3Constants.ARG_HEARTRATE_NO_TARGET) { + sample.setRawKind(ActivityKind.TYPE_NOT_WORN); + } else if (heartRate == MakibesHR3Constants.ARG_HEARTRATE_NO_READING) { + sample.setRawKind(ActivityKind.TYPE_NOT_MEASURED); + } else { + LOG.warn("invalid heart rate reading: " + heartRate); + return; + } + } + + this.addGBActivitySample(sample); + this.broadcastSample(sample); + } + + private void onReceiveHeartRateSample(int year, int month, int day, int hour, int minute, int heartRate) { + LOG.debug("received heart rate sample " + year + "-" + month + "-" + day + " " + hour + ":" + minute + " " + heartRate); + + MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); + + Calendar calendar = new GregorianCalendar(year, month - 1, day, hour, minute); + + int timeStamp = (int) (calendar.getTimeInMillis() / 1000); + + sample.setHeartRate(heartRate); + sample.setTimestamp(timeStamp); + + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); + + this.addGBActivitySample(sample); } @Override @@ -555,18 +611,9 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement arguments[i] = value[i + 6]; } - byte report = value[4]; + byte[] report = new byte[]{ value[4], value[5] }; - LOG.debug("report: " + Integer.toHexString((int) report)); - - switch (report) { - case MakibesHR3Constants.RPRT_FITNESS: - if (value.length == 17) { - this.onReceiveFitness( - (int) arguments[1] * 0xff + arguments[2] - ); - } - break; + switch (report[0]) { case MakibesHR3Constants.RPRT_REVERSE_FIND_DEVICE: this.onReverseFindDevice(arguments[0] == 0x01); break; @@ -590,6 +637,18 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.getDevice().setFirmwareVersion(((int) arguments[0]) + "." + ((int) arguments[1])); } break; + default: // Non-80 reports + if (Arrays.equals(report, MakibesHR3Constants.RPRT_FITNESS)) { + this.onReceiveFitness( + (int) arguments[1] * 0xff + arguments[2] + ); + } else if (Arrays.equals(report, MakibesHR3Constants.RPRT_HEART_RATE_SAMPLE)) { + this.onReceiveHeartRateSample( + arguments[0] + 2000, arguments[1], arguments[2], + arguments[3], arguments[4], + arguments[5]); + } + break; } } From cd3558cd50b44b69b1c0f176f3db3680711aaa9d Mon Sep 17 00:00:00 2001 From: Cre3per Date: Mon, 7 Oct 2019 17:50:32 +0200 Subject: [PATCH 09/34] makibes hr3. fixed long notifications a a nullptrexception. added vibration timeout. added step history download. --- .../makibeshr3/MakibesHR3Constants.java | 47 +++-- .../makibeshr3/MakibesHR3DeviceSupport.java | 183 ++++++++++++++---- 2 files changed, 169 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 1981bef1f..6762b68f3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -96,21 +96,23 @@ public final class MakibesHR3Constants { // heart rate public static final byte[] RPRT_HEART_RATE_SAMPLE = new byte[]{ (byte) 0x51, (byte) 0x11 }; - // year + // WearFit says "walking" in the step details. This is probably also in here, but + // I don't run :O + // year (+2000) // month // day - // hour? - // 00 - // 01 - // 39 + // hour (start of measurement. interval is 1h. Might be longer when running.) + // 00 (either used for steps or minute) + // accumulated steps (hi) + // accumulated steps (lo) // 00 // 00 - // 0d + // ?? (changes whenever steps change. Ranges from 00 to 16.) // 00 // 00 // 00 // 00 - public static final byte[] RPRT_51_20 = new byte[]{ (byte) 0x51, (byte) 0x20 }; + public static final byte[] RPRT_STEPS_SAMPLE = new byte[]{ (byte) 0x51, (byte) 0x20 }; // enable (00/01) @@ -155,23 +157,27 @@ public final class MakibesHR3Constants { // enable (00/01) public static final byte[] CMD_SET_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; - - // The times in here are probably 'from' and 'until' + // device replies with + // {@link MakibesHR3Constants#RPRT_HEART_RATE_SAMPLE} + // {@link MakibesHR3Constants#RPRT_STEPS_SAMPLE} (Only if steps are non-zero) + // {@link MakibesHR3Constants#RPRT_FITNESS} + // there are also multiple 6 * 00 reports // 00 - // year (+2000) - // month (not current! but close. Multiple of 5) - // day (not current! but close. Multiple of 5) - // 0b (A) - // 00 (B) - // year (+2000) - // month (not current! but close) - // day (not current! but close) - // 0b (this is >= (A). Multiple of 5) - // 19 (this is >= (B). Multiple of 5) + // year (+2000) steps after + // month steps after + // day steps after + // hour steps after + // minute steps after + // year (+2000) heart rate after + // month heart rate after + // day heart rate after + // hour heart rate after + // minute heart rate after public static final byte CMD_REQUEST_FITNESS = (byte) 0x51; - // this looks like a request for the heart rate history + // Manually sending this doesn't yield a reply. The heart rate history is sent in response to + // CMD_CMD_REQUEST_FITNESS. // 00 // year (+2000) // month (not current!) @@ -181,6 +187,7 @@ public final class MakibesHR3Constants { public static final byte CMD_52 = (byte) 0x52; + // vibrates 6 times public static final byte CMD_FIND_DEVICE = (byte) 0x71; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index d3271f566..675a4627a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,11 +1,10 @@ -// TODO: WearFit sometimes resets today's step count when it's used after GB. +// TODO: GB sometimes fails to connect until a connection with WearFit was made. This must be caused +// TODO: by GB, not by makibes hr3 support. + // TODO: Where can I view today's steps in GB? // TODO: GB accumulates all step samples, even if they're part of the same day. // TODO: Activity history download progress. -// TODO: Remove downloaded history from the device. - -// TODO: Request and handle step history from the device. // TODO: All the commands that aren't supported by GB should be added to device specific settings. @@ -21,6 +20,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.os.VibrationEffect; import android.os.Vibrator; import android.widget.Toast; @@ -30,7 +30,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.sql.Date; +import java.util.Date; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -73,6 +73,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3DeviceSupport.class); + private Handler mVibrationHandler = new Handler(); private Vibrator mVibrator; private BluetoothGattCharacteristic mControlCharacteristic = null; @@ -136,8 +137,16 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement break; } + String message = ""; + + if (notificationSpec.title != null) { + message += (notificationSpec.title + ": "); + } + + message += notificationSpec.body; + this.sendNotification(transactionBuilder, - sender, notificationSpec.title + ": " + notificationSpec.body); + sender, message); try { this.performConnected(transactionBuilder.getTransaction()); @@ -224,7 +233,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement if (callSpec.command == CallSpec.CALL_INCOMING) { this.sendNotification(transactionBuilder, MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_CALL, callSpec.name); } else { - this.sendNotification(transactionBuilder, MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_STOP_CALL, callSpec.name); + this.sendNotification(transactionBuilder, MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_STOP_CALL, ""); } try { @@ -325,21 +334,35 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } private void onReverseFindDevice(boolean start) { - final long[] PATTERN = new long[]{ - 100, 100, - 100, 100, - 100, 100, - 500 - }; + if (this.mVibrator.hasVibrator()) { + final long[] PATTERN = new long[]{ + 100, 100, + 100, 100, + 100, 100, + 500 + }; + + if (start) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + this.mVibrator.vibrate(VibrationEffect.createWaveform(PATTERN, 0)); + } else { + this.mVibrator.vibrate(PATTERN, 0); + } + + // In case the connection is closed while we're searching for the device. + + this.mVibrationHandler.postDelayed(new Runnable() { + @Override + public void run() { + mVibrator.cancel(); + } + }, 1100 * 6); - if (start) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - this.mVibrator.vibrate(VibrationEffect.createWaveform(PATTERN, 0)); } else { - this.mVibrator.vibrate(PATTERN, 0); + this.mVibrator.cancel(); } } else { - this.mVibrator.cancel(); + // TODO: Alternative handling. Don't use sound, the connection isn't secure. } } @@ -477,8 +500,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement // Initialize device sendUserInfo(builder); //Sync preferences - // TODO: arguments - this.requestFitness(builder, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0); + this.requestFitness(builder); gbDevice.setState(GBDevice.State.INITIALIZED); gbDevice.sendDeviceUpdateIntent(getContext()); @@ -521,20 +543,21 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } private void addGBActivitySample(MakibesHR3ActivitySample sample) { - this.addGBActivitySamples(new MakibesHR3ActivitySample[]{ sample }); + this.addGBActivitySamples(new MakibesHR3ActivitySample[]{sample}); } /** * Should only be called after the sample has been populated by * {@link MakibesHR3DeviceSupport#addGBActivitySample} or * {@link MakibesHR3DeviceSupport#addGBActivitySamples} + * * @param sample */ private void broadcastSample(MakibesHR3ActivitySample sample) { Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample) .putExtra(DeviceService.EXTRA_TIMESTAMP, sample.getTimestamp()); - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); } private void onReceiveFitness(int steps) { @@ -590,6 +613,26 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.addGBActivitySample(sample); } + /** + * The time is the start of the measurement. Each measurement lasts 1h. + */ + private void onReceiveStepsSample(int year, int month, int day, int hour, int minute, int steps) { + LOG.debug("received steps sample " + year + "-" + month + "-" + day + " " + hour + ":" + minute + " " + steps); + + MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); + + Calendar calendar = new GregorianCalendar(year, month - 1, day, hour + 1, minute); + + int timeStamp = (int) (calendar.getTimeInMillis() / 1000); + + sample.setSteps(steps); + sample.setTimestamp(timeStamp); + + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); + + this.addGBActivitySample(sample); + } + @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { @@ -611,7 +654,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement arguments[i] = value[i + 6]; } - byte[] report = new byte[]{ value[4], value[5] }; + byte[] report = new byte[]{value[4], value[5]}; switch (report[0]) { case MakibesHR3Constants.RPRT_REVERSE_FIND_DEVICE: @@ -639,14 +682,19 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement break; default: // Non-80 reports if (Arrays.equals(report, MakibesHR3Constants.RPRT_FITNESS)) { - this.onReceiveFitness( - (int) arguments[1] * 0xff + arguments[2] - ); + this.onReceiveFitness( + (int) arguments[1] * 0xff + arguments[2] + ); } else if (Arrays.equals(report, MakibesHR3Constants.RPRT_HEART_RATE_SAMPLE)) { this.onReceiveHeartRateSample( arguments[0] + 2000, arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); + } else if (Arrays.equals(report, MakibesHR3Constants.RPRT_STEPS_SAMPLE)) { + this.onReceiveStepsSample( + arguments[0] + 2000, arguments[1], arguments[2], + arguments[3], 0, + (arguments[5] * 0xff) + arguments[6]); } break; } @@ -684,7 +732,11 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement final int maxMessageLength = 20; // For every split, we need 1 byte extra. - int extraBytes = (((data.length - maxMessageLength) / maxMessageLength) + 1); + int extraBytes = 0; + + if (data.length > 20) { + extraBytes = (((data.length - maxMessageLength) / maxMessageLength) + 1); + } int totalDataLength = (data.length + extraBytes); @@ -726,24 +778,32 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this.reboot(transaction); } + /** + * Ugly because I don't like Date. + * All non-zero records after the given times will be returned via + * {@link MakibesHR3Constants#RPRT_HEART_RATE_SAMPLE}, + * {@link MakibesHR3Constants#RPRT_STEPS_SAMPLE}, + * {@link MakibesHR3Constants#RPRT_FITNESS} + */ private MakibesHR3DeviceSupport requestFitness(TransactionBuilder transaction, - int yearStart, int monthStart, int dayStart, - int a4, int a5, - int yearEnd, int monthEnd, int dayEnd, - int a9, int a10) { + int yearStepsAfter, int monthStepsAfter, int dayStepsAfter, + int hourStepsAfter, int minuteStepsAfter, + int yearHeartRateAfter, int monthHeartRateAfter, int dayHeartRateAfter, + int hourHeartRateAfter, int minuteHeartRateAfter) { byte[] data = this.craftData(MakibesHR3Constants.CMD_REQUEST_FITNESS, new byte[]{ - (byte) (yearStart - 2000), - (byte) monthStart, - (byte) dayStart, - (byte) a4, - (byte) a5, - (byte) (yearEnd - 2000), - (byte) monthEnd, - (byte) dayEnd, - (byte) a9, - (byte) a10 + (byte) 0x00, + (byte) (yearStepsAfter - 2000), + (byte) monthStepsAfter, + (byte) dayStepsAfter, + (byte) hourStepsAfter, + (byte) minuteStepsAfter, + (byte) (yearHeartRateAfter - 2000), + (byte) monthHeartRateAfter, + (byte) dayHeartRateAfter, + (byte) hourHeartRateAfter, + (byte) minuteHeartRateAfter }); transaction.write(this.mControlCharacteristic, data); @@ -751,6 +811,47 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this; } + /** + * Ugly because I don't like Date. + * All non-zero records after the given times will be returned via + * {@link MakibesHR3Constants#RPRT_HEART_RATE_SAMPLE}, + * {@link MakibesHR3Constants#RPRT_STEPS_SAMPLE}, + * {@link MakibesHR3Constants#RPRT_FITNESS} + */ + private MakibesHR3DeviceSupport requestFitness(TransactionBuilder transaction) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + + MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession()); + + MakibesHR3ActivitySample latestSample = provider.getLatestActivitySample(); + + if (latestSample == null) { + this.requestFitness(transaction, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0); + } else { + // getInstance is unnecessary, we're overriding. + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(latestSample.getTimestamp() * 1000l)); + + int year = calendar.get(Calendar.YEAR); + int month = (calendar.get(Calendar.MONTH) + 1); + int day = calendar.get(Calendar.DAY_OF_MONTH); + int hour = calendar.get(Calendar.HOUR_OF_DAY); + int minute = calendar.get(Calendar.MINUTE); + + this.requestFitness(transaction, + year, month, day, hour, minute, + year, month, day, hour, minute); + } + + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return this; + } + private MakibesHR3DeviceSupport findDevice(TransactionBuilder transaction) { transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FIND_DEVICE)); From fdffe813f2c97a4007eea26e77cec164eb7d6b6a Mon Sep 17 00:00:00 2001 From: Cre3per Date: Tue, 8 Oct 2019 13:37:25 +0200 Subject: [PATCH 10/34] makibes hr3. download progress notification (needs to be tested). --- .../gadgetbridge/service/btle/BtLEQueue.java | 2 + .../makibeshr3/MakibesHR3DeviceSupport.java | 45 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 52b6e96d3..92b56bb33 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -242,6 +242,7 @@ public final class BtLEQueue { mBluetoothGattServer.addService(service); } } + synchronized (mGattMonitor) { // connectGatt with true doesn't really work ;( too often connection problems if (GBApplication.isRunningMarshmallowOrLater()) { @@ -259,6 +260,7 @@ public final class BtLEQueue { private void setDeviceConnectionState(State newState) { LOG.debug("new device connection state: " + newState); + mGbDevice.setState(newState); mGbDevice.sendDeviceUpdateIntent(mContext); if (mConnectionLatch != null && newState == State.CONNECTED) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 675a4627a..e3b92f7f6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,11 +1,10 @@ // TODO: GB sometimes fails to connect until a connection with WearFit was made. This must be caused -// TODO: by GB, not by makibes hr3 support. +// TODO: by GB, not by makibes hr3 support. Charging the watch or attempting to pair might also +// TODO: help. This needs further research. // TODO: Where can I view today's steps in GB? // TODO: GB accumulates all step samples, even if they're part of the same day. -// TODO: Activity history download progress. - // TODO: All the commands that aren't supported by GB should be added to device specific settings. // TODO: It'd be cool if we could change the language. There's no official way to do so, but the @@ -20,6 +19,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; +import android.os.CountDownTimer; import android.os.Handler; import android.os.VibrationEffect; import android.os.Vibrator; @@ -38,6 +38,7 @@ import java.util.GregorianCalendar; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; @@ -76,6 +77,19 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private Handler mVibrationHandler = new Handler(); private Vibrator mVibrator; + private CountDownTimer mFetchCountDown = new CountDownTimer(1000, 1000) { + @Override + public void onTick(long millisUntilFinished) { + + } + + @Override + public void onFinish() { + LOG.debug("download finished"); + GB.updateTransferNotification(null, "", false, 100, getContext()); + } + }; + private BluetoothGattCharacteristic mControlCharacteristic = null; private BluetoothGattCharacteristic mReportCharacteristic = null; @@ -86,6 +100,20 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement addSupportedService(MakibesHR3Constants.UUID_SERVICE); } + /** + * Called whenever data is received to postpone the removing of the progress notification. + * @param start Start showing the notification + */ + private void fetch(boolean start) { + if (start) { + // We don't know how long the watch is going to take to reply. Keep progress at 0. + GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, 0, getContext()); + } + + this.mFetchCountDown.cancel(); + this.mFetchCountDown.start(); + } + @Override public boolean useAutoConnect() { return false; @@ -434,7 +462,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } private MakibesHR3DeviceSupport sendUserInfo(TransactionBuilder builder) { - syncPreferences(builder); + this.syncPreferences(builder); return this; } @@ -455,11 +483,14 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement activityUser.getWeightKg(), activityUser.getStepsGoal() / 1000); + // setLanguage(transaction); // setScreenTime(transaction); // setUnit(transaction); // setAllDayHeart(transaction); + this.fetch(true); + return this; } @@ -482,6 +513,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, 0, getContext()); + gbDevice.setState(GBDevice.State.INITIALIZING); gbDevice.sendDeviceUpdateIntent(getContext()); @@ -644,6 +677,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement if (data.length < 6) return true; + this.fetch(false); + UUID characteristicUuid = characteristic.getUuid(); if (characteristicUuid.equals(mReportCharacteristic.getUuid())) { @@ -791,6 +826,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement int yearHeartRateAfter, int monthHeartRateAfter, int dayHeartRateAfter, int hourHeartRateAfter, int minuteHeartRateAfter) { + this.fetch(true); + byte[] data = this.craftData(MakibesHR3Constants.CMD_REQUEST_FITNESS, new byte[]{ (byte) 0x00, From ec88a7d8e5dc0e4603babc6998171c277e2cb86f Mon Sep 17 00:00:00 2001 From: Cre3per Date: Tue, 8 Oct 2019 15:23:46 +0200 Subject: [PATCH 11/34] makibes hr3. fixed step counting (needs multi-day testing). fixed signed byte to int conversion. --- .../makibeshr3/MakibesHR3DeviceSupport.java | 115 +++++++++++++----- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index e3b92f7f6..c67f6f002 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -2,9 +2,6 @@ // TODO: by GB, not by makibes hr3 support. Charging the watch or attempting to pair might also // TODO: help. This needs further research. -// TODO: Where can I view today's steps in GB? -// TODO: GB accumulates all step samples, even if they're part of the same day. - // TODO: All the commands that aren't supported by GB should be added to device specific settings. // TODO: It'd be cool if we could change the language. There's no official way to do so, but the @@ -25,6 +22,7 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.widget.Toast; +import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; @@ -35,6 +33,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.List; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -102,6 +101,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement /** * Called whenever data is received to postpone the removing of the progress notification. + * * @param start Start showing the notification */ private void fetch(boolean start) { @@ -119,6 +119,52 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return false; } + /** + * + * @param timeStamp seconds + */ + private void getDayStartEnd(int timeStamp, Calendar start, Calendar end) { + final int DAY = (24 * 60 * 60); + + int timeStampStart = ((timeStamp / DAY) * DAY); + int timeStampEnd = (timeStampStart + DAY); + + start.setTimeInMillis(timeStampStart * 1000l); + end.setTimeInMillis(timeStampEnd * 1000l); + } + + /** + * @param timeStamp Time stamp at some point during the requested day. + */ + private int getStepsOnDay(int timeStamp) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + + Calendar dayStart = new GregorianCalendar(); + Calendar dayEnd = new GregorianCalendar(); + + this.getDayStartEnd(timeStamp, dayStart, dayEnd); + + MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession()); + + List samples = provider.getAllActivitySamples( + (int) (dayStart.getTimeInMillis() / 1000l), + (int) (dayEnd.getTimeInMillis() / 1000l)); + + int totalSteps = 0; + + for (MakibesHR3ActivitySample sample : samples) { + totalSteps += sample.getSteps(); + } + + return totalSteps; + + } catch (Exception ex) { + LOG.error(ex.getMessage()); + + return 0; + } + } + public MakibesHR3ActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); sample.setDevice(device); @@ -596,13 +642,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private void onReceiveFitness(int steps) { LOG.info("steps: " + steps); - MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); - - sample.setSteps(steps); - sample.setTimestamp((int) (System.currentTimeMillis() / 1000)); - - sample.setRawKind(ActivityKind.TYPE_ACTIVITY); - this.addGBActivitySample(sample); + this.onReceiveStepsSample(steps); } private void onReceiveHeartRate(int heartRate) { @@ -646,24 +686,41 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.addGBActivitySample(sample); } + private void onReceiveStepsSample(int timeStamp, int steps) { + MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); + + // We need to subtract the day's total step count thus far. + int dayStepCount = this.getStepsOnDay(timeStamp); + + int newSteps = (steps - dayStepCount); + + if (newSteps > 0) { + LOG.debug("adding " + newSteps + " steps"); + + sample.setSteps(steps - dayStepCount); + sample.setTimestamp(timeStamp); + + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); + + this.addGBActivitySample(sample); + } + } + /** * The time is the start of the measurement. Each measurement lasts 1h. */ private void onReceiveStepsSample(int year, int month, int day, int hour, int minute, int steps) { LOG.debug("received steps sample " + year + "-" + month + "-" + day + " " + hour + ":" + minute + " " + steps); - MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample(); - Calendar calendar = new GregorianCalendar(year, month - 1, day, hour + 1, minute); int timeStamp = (int) (calendar.getTimeInMillis() / 1000); - sample.setSteps(steps); - sample.setTimestamp(timeStamp); + this.onReceiveStepsSample(timeStamp, steps); + } - sample.setRawKind(ActivityKind.TYPE_ACTIVITY); - - this.addGBActivitySample(sample); + private void onReceiveStepsSample(int steps) { + this.onReceiveStepsSample((int) (Calendar.getInstance().getTimeInMillis() / 1000l), steps); } @Override @@ -704,7 +761,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement if (arguments.length == 2) { GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo(); - batteryInfo.level = (short) arguments[1]; + batteryInfo.level = (short) (arguments[1] & 0xff); batteryInfo.state = ((arguments[0] == 0x01) ? BatteryState.BATTERY_CHARGING : BatteryState.BATTERY_NORMAL); this.handleGBDeviceEvent(batteryInfo); @@ -712,24 +769,25 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement break; case MakibesHR3Constants.RPRT_SOFTWARE: if (arguments.length == 11) { - this.getDevice().setFirmwareVersion(((int) arguments[0]) + "." + ((int) arguments[1])); + this.getDevice().setFirmwareVersion(((int) (arguments[0] & 0xff)) + "." + (arguments[1] & 0xff)); } break; default: // Non-80 reports if (Arrays.equals(report, MakibesHR3Constants.RPRT_FITNESS)) { + int steps = (arguments[1] & 0xff) * 0x100 + (arguments[2] & 0xff); this.onReceiveFitness( - (int) arguments[1] * 0xff + arguments[2] + steps ); } else if (Arrays.equals(report, MakibesHR3Constants.RPRT_HEART_RATE_SAMPLE)) { this.onReceiveHeartRateSample( - arguments[0] + 2000, arguments[1], arguments[2], - arguments[3], arguments[4], - arguments[5]); + (arguments[0] & 0xff) + 2000, (arguments[1] & 0xff), (arguments[2] & 0xff), + (arguments[3] & 0xff), (arguments[4] & 0xff), + (arguments[5] & 0xff)); } else if (Arrays.equals(report, MakibesHR3Constants.RPRT_STEPS_SAMPLE)) { this.onReceiveStepsSample( - arguments[0] + 2000, arguments[1], arguments[2], - arguments[3], 0, - (arguments[5] * 0xff) + arguments[6]); + (arguments[0] & 0xff) + 2000, (arguments[1] & 0xff), (arguments[2] & 0xff), + (arguments[3] & 0xff), 0, + ((arguments[5] & 0xff) * 0x100) + (arguments[6] & 0xff)); } break; } @@ -862,13 +920,12 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement MakibesHR3ActivitySample latestSample = provider.getLatestActivitySample(); - if (latestSample == null) { + if (true || latestSample == null) { this.requestFitness(transaction, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } else { - // getInstance is unnecessary, we're overriding. - Calendar calendar = Calendar.getInstance(); + Calendar calendar = new GregorianCalendar(); calendar.setTime(new Date(latestSample.getTimestamp() * 1000l)); int year = calendar.get(Calendar.YEAR); From 4c98d8537fda2ca8e0426a29279341f76d421313 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Tue, 8 Oct 2019 16:07:43 +0200 Subject: [PATCH 12/34] makibes hr3. added hud to device settings. --- .../makibeshr3/MakibesHR3Constants.java | 9 ++- .../makibeshr3/MakibesHR3Coordinator.java | 17 ++++- .../makibeshr3/MakibesHR3DeviceSupport.java | 72 ++++++++++--------- 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 6762b68f3..16ef7b797 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -21,6 +21,9 @@ import java.util.UUID; public final class MakibesHR3Constants { + // TODO: This doesn't belong here, but I don't want to touch other files to avoid + // TODO: breaking someone else's device support. + public static final String PREF_HEADS_UP_SCREEN = "activate_display_on_lift_wrist"; public static final UUID UUID_SERVICE = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); @@ -72,7 +75,7 @@ public final class MakibesHR3Constants { public static final byte[] RPRT_SINGLE_BLOOD_OXYGEN = new byte[]{ (byte) 0x31, (byte) 0x11 }; - // steps take up more bytes. I don't know which ones yet. + // steps might take up more bytes. I don't know which ones and I won't walk that much. // Only sent after we send CMD_51 // 00 (maybe also used for steps) // [steps hi] @@ -87,6 +90,7 @@ public final class MakibesHR3Constants { // 00 public static final byte[] RPRT_FITNESS = new byte[]{ (byte) 0x51, 0x08 }; + // year (+2000) // month // day @@ -96,6 +100,7 @@ public final class MakibesHR3Constants { // heart rate public static final byte[] RPRT_HEART_RATE_SAMPLE = new byte[]{ (byte) 0x51, (byte) 0x11 }; + // WearFit says "walking" in the step details. This is probably also in here, but // I don't run :O // year (+2000) @@ -179,7 +184,7 @@ public final class MakibesHR3Constants { // Manually sending this doesn't yield a reply. The heart rate history is sent in response to // CMD_CMD_REQUEST_FITNESS. // 00 - // year (+2000) + // year (+2000) (probably not current) // month (not current!) // day (not current!) // hour (current) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index e5ddbc4a7..abe80b5fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -39,7 +39,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySampleDao; -import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -62,6 +61,19 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { } } + public static boolean shouldEnableHeadsUpScreen(SharedPreferences sharedPrefs) { + String liftMode = sharedPrefs.getString(MakibesHR3Constants.PREF_HEADS_UP_SCREEN, getContext().getString(R.string.p_on)); + + // Makibes HR3 doesn't support scheduled intervals. Treat it as "on". + return (liftMode != getContext().getString(R.string.p_off)); + } + + public static boolean shouldEnableHeadsUpScreen(String deviceAddress) { + SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); + + return shouldEnableHeadsUpScreen(sharedPrefs); + } + public static byte getTimeMode(String deviceAddress) { SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); @@ -182,7 +194,8 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { @Override public int[] getSupportedDeviceSpecificSettings(GBDevice device) { return new int[]{ - R.xml.devicesettings_timeformat + R.xml.devicesettings_timeformat, + R.xml.devicesettings_liftwrist_display }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index c67f6f002..f7085939c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,11 +1,13 @@ // TODO: GB sometimes fails to connect until a connection with WearFit was made. This must be caused -// TODO: by GB, not by makibes hr3 support. Charging the watch or attempting to pair might also +// TODO: by GB, not by Makibes hr3 support. Charging the watch or attempting to pair might also // TODO: help. This needs further research. // TODO: All the commands that aren't supported by GB should be added to device specific settings. // TODO: It'd be cool if we could change the language. There's no official way to do so, but the -// TODO: watch is sold as chinese/english. Screen on time would be nice too. +// TODO: watch is sold as chinese/english. Screen-on-time would be nice too. + +// TODO: Firmware upgrades. package nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3; @@ -22,7 +24,6 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.widget.Toast; -import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; @@ -38,6 +39,7 @@ import java.util.UUID; 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.GBDeviceEventBatteryInfo; @@ -120,7 +122,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } /** - * * @param timeStamp seconds */ private void getDayStartEnd(int timeStamp, Calendar start, Calendar end) { @@ -129,8 +130,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement int timeStampStart = ((timeStamp / DAY) * DAY); int timeStampEnd = (timeStampStart + DAY); - start.setTimeInMillis(timeStampStart * 1000l); - end.setTimeInMillis(timeStampEnd * 1000l); + start.setTimeInMillis(timeStampStart * 1000L); + end.setTimeInMillis(timeStampEnd * 1000L); } /** @@ -147,8 +148,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession()); List samples = provider.getAllActivitySamples( - (int) (dayStart.getTimeInMillis() / 1000l), - (int) (dayEnd.getTimeInMillis() / 1000l)); + (int) (dayStart.getTimeInMillis() / 1000L), + (int) (dayEnd.getTimeInMillis() / 1000L)); int totalSteps = 0; @@ -507,18 +508,12 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } - private MakibesHR3DeviceSupport sendUserInfo(TransactionBuilder builder) { - this.syncPreferences(builder); - - return this; - } - - private MakibesHR3DeviceSupport syncPreferences(TransactionBuilder transaction) { + private void syncPreferences(TransactionBuilder transaction) { this.setTimeMode(transaction); this.setDateTime(transaction); - // setDayOfWeek(transaction); - this.setTimeMode(transaction); + + this.setHeadsUpScreen(transaction); ActivityUser activityUser = new ActivityUser(); @@ -529,23 +524,17 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement activityUser.getWeightKg(), activityUser.getStepsGoal() / 1000); - - // setLanguage(transaction); - // setScreenTime(transaction); - // setUnit(transaction); - // setAllDayHeart(transaction); - this.fetch(true); - - return this; } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { LOG.debug(key + " changed"); TransactionBuilder transactionBuilder = this.createTransactionBuilder("onSharedPreferenceChanged"); - if (key.equals(PREF_TIMEFORMAT)) { + if (key.equals(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT)) { this.setTimeMode(transactionBuilder); + } else if (key.equals(MakibesHR3Constants.PREF_HEADS_UP_SCREEN)) { + this.setHeadsUpScreen(transactionBuilder); } else { return; } @@ -577,7 +566,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement builder.write(this.mControlCharacteristic, new byte[]{0x01, 0x00}); // Initialize device - sendUserInfo(builder); //Sync preferences + this.syncPreferences(builder); this.requestFitness(builder); @@ -629,8 +618,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement * Should only be called after the sample has been populated by * {@link MakibesHR3DeviceSupport#addGBActivitySample} or * {@link MakibesHR3DeviceSupport#addGBActivitySamples} - * - * @param sample */ private void broadcastSample(MakibesHR3ActivitySample sample) { Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) @@ -1018,16 +1005,33 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this; } - private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transaction) { - byte value = MakibesHR3Coordinator.getTimeMode(getDevice().getAddress()); + private MakibesHR3DeviceSupport setHeadsUpScreen(TransactionBuilder transactionBuilder, boolean enable) { + byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_HEADS_UP_SCREEN, + new byte[]{(byte) (enable ? 0x01 : 0x00)}); - byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_TIMEMODE, new byte[]{value}); - - transaction.write(this.mControlCharacteristic, data); + transactionBuilder.write(this.mControlCharacteristic, data); return this; } + private MakibesHR3DeviceSupport setHeadsUpScreen(TransactionBuilder transactionBuilder) { + return this.setHeadsUpScreen(transactionBuilder, + MakibesHR3Coordinator.shouldEnableHeadsUpScreen(this.getDevice().getAddress())); + } + + private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transactionBuilder, byte timeMode) { + byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_TIMEMODE, new byte[]{timeMode}); + + transactionBuilder.write(this.mControlCharacteristic, data); + + return this; + } + + private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transactionBuilder) { + return this.setTimeMode(transactionBuilder, + MakibesHR3Coordinator.getTimeMode(this.getDevice().getAddress())); + } + private MakibesHR3DeviceSupport setEnableRealTimeHeartRate(TransactionBuilder transaction, boolean enable) { byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_REAL_TIME_HEART_RATE, new byte[]{(byte) (enable ? 0x01 : 0x00)}); From 1262970494d941e1d2afb3a9b97ccfcb02473c87 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Tue, 8 Oct 2019 16:46:33 +0200 Subject: [PATCH 13/34] makibes hr3. added lost reminder support. --- .../makibeshr3/MakibesHR3Constants.java | 1 + .../makibeshr3/MakibesHR3Coordinator.java | 30 +++++------- .../makibeshr3/MakibesHR3DeviceSupport.java | 48 +++++++++++++++---- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 16ef7b797..b06de6ec3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -24,6 +24,7 @@ public final class MakibesHR3Constants { // TODO: This doesn't belong here, but I don't want to touch other files to avoid // TODO: breaking someone else's device support. public static final String PREF_HEADS_UP_SCREEN = "activate_display_on_lift_wrist"; + public static final String PREF_LOST_REMINDER = "disconnect_notification"; public static final UUID UUID_SERVICE = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index abe80b5fc..8ac310f80 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -29,7 +29,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.greenrobot.dao.query.QueryBuilder; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; @@ -51,15 +50,6 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3Coordinator.class); - public static byte getTimeMode(SharedPreferences sharedPrefs) { - String timeMode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); - - if (timeMode.equals(getContext().getString(R.string.p_timeformat_24h))) { - return MakibesHR3Constants.ARG_SET_TIMEMODE_24H; - } else { - return MakibesHR3Constants.ARG_SET_TIMEMODE_12H; - } - } public static boolean shouldEnableHeadsUpScreen(SharedPreferences sharedPrefs) { String liftMode = sharedPrefs.getString(MakibesHR3Constants.PREF_HEADS_UP_SCREEN, getContext().getString(R.string.p_on)); @@ -68,16 +58,21 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { return (liftMode != getContext().getString(R.string.p_off)); } - public static boolean shouldEnableHeadsUpScreen(String deviceAddress) { - SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); + public static boolean shouldEnableLostReminder(SharedPreferences sharedPrefs) { + String lostReminder = sharedPrefs.getString(MakibesHR3Constants.PREF_LOST_REMINDER, getContext().getString(R.string.p_on)); - return shouldEnableHeadsUpScreen(sharedPrefs); + // Makibes HR3 doesn't support scheduled intervals. Treat it as "on". + return (lostReminder != getContext().getString(R.string.p_off)); } - public static byte getTimeMode(String deviceAddress) { - SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); + public static byte getTimeMode(SharedPreferences sharedPrefs) { + String timeMode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); - return getTimeMode(sharedPrefs); + if (timeMode.equals(getContext().getString(R.string.p_timeformat_24h))) { + return MakibesHR3Constants.ARG_SET_TIMEMODE_24H; + } else { + return MakibesHR3Constants.ARG_SET_TIMEMODE_12H; + } } @NonNull @@ -195,7 +190,8 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { public int[] getSupportedDeviceSpecificSettings(GBDevice device) { return new int[]{ R.xml.devicesettings_timeformat, - R.xml.devicesettings_liftwrist_display + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_disconnectnotification }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index f7085939c..0933ac88d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,9 +1,11 @@ // TODO: GB sometimes fails to connect until a connection with WearFit was made. This must be caused -// TODO: by GB, not by Makibes hr3 support. Charging the watch or attempting to pair might also -// TODO: help. This needs further research. +// TODO: by GB, not by Makibes hr3 support. Charging the watch, attempting to pair, delete and +// TODO: re-add might also help. This needs further research. // TODO: All the commands that aren't supported by GB should be added to device specific settings. +// TODO: GB doesn't always display the step count even though there's a sample in the db. + // TODO: It'd be cool if we could change the language. There's no official way to do so, but the // TODO: watch is sold as chinese/english. Screen-on-time would be nice too. @@ -65,6 +67,7 @@ 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.Transaction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -510,10 +513,13 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private void syncPreferences(TransactionBuilder transaction) { - this.setTimeMode(transaction); + SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress()); + + this.setTimeMode(transaction, sharedPreferences); this.setDateTime(transaction); - this.setHeadsUpScreen(transaction); + this.setHeadsUpScreen(transaction, sharedPreferences); + this.setLostReminder(transaction, sharedPreferences); ActivityUser activityUser = new ActivityUser(); @@ -529,12 +535,20 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { LOG.debug(key + " changed"); + + if (!this.isConnected()) { + LOG.debug("ignoring change, we're disconnected"); + return; + } + TransactionBuilder transactionBuilder = this.createTransactionBuilder("onSharedPreferenceChanged"); if (key.equals(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT)) { - this.setTimeMode(transactionBuilder); + this.setTimeMode(transactionBuilder, sharedPreferences); } else if (key.equals(MakibesHR3Constants.PREF_HEADS_UP_SCREEN)) { - this.setHeadsUpScreen(transactionBuilder); + this.setHeadsUpScreen(transactionBuilder, sharedPreferences); + } else if (key.equals(MakibesHR3Constants.PREF_LOST_REMINDER)) { + this.setLostReminder(transactionBuilder, sharedPreferences); } else { return; } @@ -1014,9 +1028,23 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this; } - private MakibesHR3DeviceSupport setHeadsUpScreen(TransactionBuilder transactionBuilder) { + private MakibesHR3DeviceSupport setHeadsUpScreen(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { return this.setHeadsUpScreen(transactionBuilder, - MakibesHR3Coordinator.shouldEnableHeadsUpScreen(this.getDevice().getAddress())); + MakibesHR3Coordinator.shouldEnableHeadsUpScreen(sharedPreferences)); + } + + private MakibesHR3DeviceSupport setLostReminder(TransactionBuilder transactionBuilder, boolean enable) { + byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_LOST_REMINDER, + new byte[]{(byte) (enable ? 0x01 : 0x00)}); + + transactionBuilder.write(this.mControlCharacteristic, data); + + return this; + } + + private MakibesHR3DeviceSupport setLostReminder(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { + return this.setLostReminder(transactionBuilder, + MakibesHR3Coordinator.shouldEnableLostReminder(sharedPreferences)); } private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transactionBuilder, byte timeMode) { @@ -1027,9 +1055,9 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this; } - private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transactionBuilder) { + private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { return this.setTimeMode(transactionBuilder, - MakibesHR3Coordinator.getTimeMode(this.getDevice().getAddress())); + MakibesHR3Coordinator.getTimeMode(sharedPreferences)); } private MakibesHR3DeviceSupport setEnableRealTimeHeartRate(TransactionBuilder transaction, boolean enable) { From 5570ac8349a6b432948a930112d718f35dc52f8f Mon Sep 17 00:00:00 2001 From: Cre3per Date: Tue, 8 Oct 2019 17:18:41 +0200 Subject: [PATCH 14/34] makibes hr3. added fakeBattery. fixed string comparison using operator. --- .../makibeshr3/MakibesHR3Coordinator.java | 4 ++-- .../makibeshr3/MakibesHR3DeviceSupport.java | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index 8ac310f80..d0aff0442 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -55,14 +55,14 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { String liftMode = sharedPrefs.getString(MakibesHR3Constants.PREF_HEADS_UP_SCREEN, getContext().getString(R.string.p_on)); // Makibes HR3 doesn't support scheduled intervals. Treat it as "on". - return (liftMode != getContext().getString(R.string.p_off)); + return !liftMode.equals(getContext().getString(R.string.p_off)); } public static boolean shouldEnableLostReminder(SharedPreferences sharedPrefs) { String lostReminder = sharedPrefs.getString(MakibesHR3Constants.PREF_LOST_REMINDER, getContext().getString(R.string.p_on)); // Makibes HR3 doesn't support scheduled intervals. Treat it as "on". - return (lostReminder != getContext().getString(R.string.p_off)); + return !lostReminder.equals(getContext().getString(R.string.p_off)); } public static byte getTimeMode(SharedPreferences sharedPrefs) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 0933ac88d..8249f0de0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -4,8 +4,6 @@ // TODO: All the commands that aren't supported by GB should be added to device specific settings. -// TODO: GB doesn't always display the step count even though there's a sample in the db. - // TODO: It'd be cool if we could change the language. There's no official way to do so, but the // TODO: watch is sold as chinese/english. Screen-on-time would be nice too. @@ -560,8 +558,26 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } } + /** + * Use to show the battery icon in the device card. + * If the icon shows up later, the user might be trying to tap one thing but the battery icon + * will shift everything. + * This is hacky. There should be a "supportsBattery" function in the coordinator that displays + * the battery icon before the battery level is received. + */ + private void fakeBattery() { + GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo(); + + batteryInfo.level = 100; + batteryInfo.state = BatteryState.UNKNOWN; + + this.handleGBDeviceEvent(batteryInfo); + } + @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + this.fakeBattery(); + GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, 0, getContext()); gbDevice.setState(GBDevice.State.INITIALIZING); From 4437220c5419f3f22c2d0c820d7feea2f4eeba4f Mon Sep 17 00:00:00 2001 From: Cre3per Date: Wed, 9 Oct 2019 15:42:03 +0200 Subject: [PATCH 15/34] makibes hr3. added quite hours. --- .../makibeshr3/MakibesHR3Constants.java | 3 + .../makibeshr3/MakibesHR3Coordinator.java | 36 ++++++++++- .../makibeshr3/MakibesHR3DeviceSupport.java | 59 +++++++++++++++---- app/src/main/res/values/arrays.xml | 9 +++ .../devicesettings_donotdisturb_no_auto.xml | 34 +++++++++++ 5 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/xml/devicesettings_donotdisturb_no_auto.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index b06de6ec3..4bf250bea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -25,6 +25,9 @@ public final class MakibesHR3Constants { // TODO: breaking someone else's device support. public static final String PREF_HEADS_UP_SCREEN = "activate_display_on_lift_wrist"; public static final String PREF_LOST_REMINDER = "disconnect_notification"; + public static final String PREF_DO_NOT_DISTURB = "do_not_disturb_no_auto"; + public static final String PREF_DO_NOT_DISTURB_START = "do_not_disturb_no_auto_start"; + public static final String PREF_DO_NOT_DISTURB_END = "do_not_disturb_no_auto_end"; public static final UUID UUID_SERVICE = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index d0aff0442..105d929f4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -16,6 +16,10 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; import android.app.Activity; import android.content.Context; @@ -35,6 +39,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySampleDao; @@ -75,6 +80,34 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { } } + /** + * @param startOut out Only hour/minute are used. + * @param endOut out Only hour/minute are used. + * @return True if quite hours are enabled. + */ + public static boolean getQuiteHours(SharedPreferences sharedPrefs, Calendar startOut, Calendar endOut) { + String doNotDisturb = sharedPrefs.getString(MakibesHR3Constants.PREF_DO_NOT_DISTURB, getContext().getString(R.string.p_off)); + + if (doNotDisturb.equals(getContext().getString(R.string.p_off))) { + return false; + } else { + String start = sharedPrefs.getString(MakibesHR3Constants.PREF_DO_NOT_DISTURB_START, "00:00"); + String end = sharedPrefs.getString(MakibesHR3Constants.PREF_DO_NOT_DISTURB_END, "00:00"); + + DateFormat df = new SimpleDateFormat("HH:mm"); + + try { + startOut.setTime(df.parse(start)); + endOut.setTime(df.parse(end)); + + return true; + } catch (Exception e) { + LOG.error("Unexpected exception in MiBand2Coordinator.getTime: " + e.getMessage()); + return false; + } + } + } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { @@ -191,7 +224,8 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { return new int[]{ R.xml.devicesettings_timeformat, R.xml.devicesettings_liftwrist_display, - R.xml.devicesettings_disconnectnotification + R.xml.devicesettings_disconnectnotification, + R.xml.devicesettings_donotdisturb_no_auto }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index 8249f0de0..e06cc605d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -1,13 +1,17 @@ // TODO: GB sometimes fails to connect until a connection with WearFit was made. This must be caused // TODO: by GB, not by Makibes hr3 support. Charging the watch, attempting to pair, delete and -// TODO: re-add might also help. This needs further research. +// TODO: re-add, scan for devices and go back, might also help. This needs further research. // TODO: All the commands that aren't supported by GB should be added to device specific settings. // TODO: It'd be cool if we could change the language. There's no official way to do so, but the // TODO: watch is sold as chinese/english. Screen-on-time would be nice too. -// TODO: Firmware upgrades. +// TODO: Firmware upgrades. WearFit tries to connect to Wake up Technology at +// TODO: http://47.112.119.52/app.php/Api/hardUpdate/type/55 +// TODO: But that server resets the connection. +// TODO: The host is supposed to be www.iwhop.com, but that domain no longer exists. +// TODO: I think /app.php is missing a closing php tag. package nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3; @@ -29,10 +33,10 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; @@ -65,13 +69,10 @@ 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.Transaction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.GB; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT; - public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implements SharedPreferences.OnSharedPreferenceChangeListener { private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3DeviceSupport.class); @@ -79,7 +80,9 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private Handler mVibrationHandler = new Handler(); private Vibrator mVibrator; - private CountDownTimer mFetchCountDown = new CountDownTimer(1000, 1000) { + // The delay must be at least as long as it takes the watch to respond. + // Reordering the requests could maybe reduce the delay, but this works fine too. + private CountDownTimer mFetchCountDown = new CountDownTimer(2000, 2000) { @Override public void onTick(long millisUntilFinished) { @@ -515,6 +518,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.setTimeMode(transaction, sharedPreferences); this.setDateTime(transaction); + this.setQuiteHours(transaction, sharedPreferences); this.setHeadsUpScreen(transaction, sharedPreferences); this.setLostReminder(transaction, sharedPreferences); @@ -547,6 +551,10 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.setHeadsUpScreen(transactionBuilder, sharedPreferences); } else if (key.equals(MakibesHR3Constants.PREF_LOST_REMINDER)) { this.setLostReminder(transactionBuilder, sharedPreferences); + } else if (key.equals(MakibesHR3Constants.PREF_DO_NOT_DISTURB) || + key.equals(MakibesHR3Constants.PREF_DO_NOT_DISTURB_START) || + key.equals(MakibesHR3Constants.PREF_DO_NOT_DISTURB_END)) { + this.setQuiteHours(transactionBuilder, sharedPreferences); } else { return; } @@ -901,8 +909,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement int yearHeartRateAfter, int monthHeartRateAfter, int dayHeartRateAfter, int hourHeartRateAfter, int minuteHeartRateAfter) { - this.fetch(true); - byte[] data = this.craftData(MakibesHR3Constants.CMD_REQUEST_FITNESS, new byte[]{ (byte) 0x00, @@ -920,6 +926,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement transaction.write(this.mControlCharacteristic, data); + this.fetch(true); + return this; } @@ -937,10 +945,10 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement MakibesHR3ActivitySample latestSample = provider.getLatestActivitySample(); - if (true || latestSample == null) { + if (latestSample == null) { this.requestFitness(transaction, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0); + 2000, 0, 0, 0, 0, + 2000, 0, 0, 0, 0); } else { Calendar calendar = new GregorianCalendar(); calendar.setTime(new Date(latestSample.getTimestamp() * 1000l)); @@ -1044,6 +1052,33 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement return this; } + private MakibesHR3DeviceSupport setQuiteHours(TransactionBuilder transactionBuilder, + boolean enable, + int hourStart, int minuteStart, + int hourEnd, int minuteEnd) { + byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_QUITE_HOURS, new byte[]{ + (byte) (enable ? 0x01 : 0x00), + (byte) hourStart, (byte) minuteStart, + (byte) hourEnd, (byte) minuteEnd + }); + + transactionBuilder.write(this.mControlCharacteristic, data); + + return this; + } + + private MakibesHR3DeviceSupport setQuiteHours(TransactionBuilder transactionBuilder, + SharedPreferences sharedPreferences) { + + Calendar start = new GregorianCalendar(); + Calendar end = new GregorianCalendar(); + boolean enable = MakibesHR3Coordinator.getQuiteHours(sharedPreferences, start, end); + + return this.setQuiteHours(transactionBuilder, enable, + start.get(Calendar.HOUR_OF_DAY), start.get(Calendar.MINUTE), + end.get(Calendar.HOUR_OF_DAY), end.get(Calendar.MINUTE)); + } + private MakibesHR3DeviceSupport setHeadsUpScreen(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { return this.setHeadsUpScreen(transactionBuilder, MakibesHR3Coordinator.shouldEnableHeadsUpScreen(sharedPreferences)); diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 326b1f064..13f301202 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -162,6 +162,15 @@ MM/dd/yyyy + + @string/mi2_dnd_off + @string/mi2_dnd_scheduled + + + @string/p_off + @string/p_scheduled + + @string/mi2_dnd_off @string/mi2_dnd_automatic diff --git a/app/src/main/res/xml/devicesettings_donotdisturb_no_auto.xml b/app/src/main/res/xml/devicesettings_donotdisturb_no_auto.xml new file mode 100644 index 000000000..f30f0ec42 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_donotdisturb_no_auto.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + From ecd1595e8454e3e1dfd4d2607383f18321422a27 Mon Sep 17 00:00:00 2001 From: Cre3per Date: Wed, 9 Oct 2019 15:56:01 +0200 Subject: [PATCH 16/34] bumped version and added makibes hr3 to readme. --- CHANGELOG.md | 6 ++++++ README.md | 1 + app/build.gradle | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcfca102d..b376aaf5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ### Changelog +#### Version 0.36.4 +* Makibes HR3: Adjusted alarm slot count +* Makibes HR3: Implemented device deletion, heart rate, steps, download progress notification, smart wake up, disconnect notification, quite hours +* Makibes HR3: Fixed medium-length notifications not being displayed +* Makibes HR3: Added a timeout to reverse find device + #### Version 0.36.3 * Basic Makibes HR3 support diff --git a/README.md b/README.md index 9cbd87cd6..1513a9c47 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ Please [this wiki article](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki * Andreas Böhler (Casio GB-6900B) * Jean-François Greffier (Mi Scale 2) * Johannes Schmitt (BFH-16) +* Lukas Schwichtenberg (Makibes HR3) ## Contribute diff --git a/app/build.gradle b/app/build.gradle index 85553c426..56233071d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { targetSdkVersion 27 // Note: always bump BOTH versionCode and versionName! - versionName "0.36.3" - versionCode 158 + versionName "0.36.4" + versionCode 159 vectorDrawables.useSupportLibrary = true } buildTypes { From c4f4f5081df709c68465f9beaffff63c03bbe73c Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 10 Oct 2019 11:32:52 +0200 Subject: [PATCH 17/34] added vibrator to FindPhoneActivity. added device settings for find phone. makibes hr3 now uses GBDeviceEventFindPhone. --- .../activities/FindPhoneActivity.java | 31 ++++++++- .../DeviceSpecificSettingsFragment.java | 17 ++++- .../makibeshr3/MakibesHR3Constants.java | 2 + .../makibeshr3/MakibesHR3Coordinator.java | 38 ++++++++++- .../makibeshr3/MakibesHR3DeviceSupport.java | 65 +++++++++---------- .../main/res/drawable/ic_find_lost_phone.xml | 28 ++++++++ app/src/main/res/values/arrays.xml | 12 ++++ app/src/main/res/values/strings.xml | 5 ++ .../res/xml/devicesettings_find_phone.xml | 24 +++++++ 9 files changed, 181 insertions(+), 41 deletions(-) create mode 100644 app/src/main/res/drawable/ic_find_lost_phone.xml create mode 100644 app/src/main/res/xml/devicesettings_find_phone.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java index dc47d2c2c..cb2a69d6e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java @@ -25,7 +25,10 @@ import android.media.AudioManager; import android.media.MediaPlayer; import android.media.RingtoneManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.view.View; import android.widget.Button; @@ -58,6 +61,7 @@ public class FindPhoneActivity extends AbstractGBActivity { } }; + Vibrator mVibrator; AudioManager mAudioManager; int userVolume; MediaPlayer mp; @@ -79,10 +83,26 @@ public class FindPhoneActivity extends AbstractGBActivity { finish(); } }); + + vibrate(); playRingtone(); } - public void playRingtone(){ + private void vibrate(){ + mVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); + + long[] vibrationPattern = new long[]{ 1000, 1000 }; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + VibrationEffect vibrationEffect = VibrationEffect.createWaveform(vibrationPattern, 0); + + mVibrator.vibrate(vibrationEffect); + } else { + mVibrator.vibrate(vibrationPattern, 0); + } + } + + private void playRingtone(){ mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); if (mAudioManager != null) { userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM); @@ -107,7 +127,11 @@ public class FindPhoneActivity extends AbstractGBActivity { } } - public void stopSound() { + private void stopVibration() { + mVibrator.cancel(); + } + + private void stopSound() { mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, userVolume, AudioManager.FLAG_PLAY_SOUND); mp.stop(); mp.reset(); @@ -116,7 +140,10 @@ public class FindPhoneActivity extends AbstractGBActivity { @Override protected void onDestroy() { super.onDestroy(); + + stopVibration(); stopSound(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); unregisterReceiver(mReceiver); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index cb1fe6c63..90d75ef58 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import java.util.Objects; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference; @@ -369,15 +370,25 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat { }); } - EditTextPreference pref = findPreference(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); - if (pref != null) { - pref.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() { + EditTextPreference mibandTimeOffset = findPreference(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); + if (mibandTimeOffset != null) { + mibandTimeOffset.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() { @Override public void onBindEditText(@NonNull EditText editText) { editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED); } }); } + + EditTextPreference findPhoneDuration = findPreference(MakibesHR3Constants.PREF_FIND_PHONE_DURATION); + if (findPhoneDuration != null) { + findPhoneDuration.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() { + @Override + public void onBindEditText(@NonNull EditText editText) { + editText.setInputType(InputType.TYPE_CLASS_NUMBER); + } + }); + } } static DeviceSpecificSettingsFragment newInstance(String settingsFileSuffix, @NonNull int[] supportedSettings) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java index 4bf250bea..7a717c18b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Constants.java @@ -28,6 +28,8 @@ public final class MakibesHR3Constants { public static final String PREF_DO_NOT_DISTURB = "do_not_disturb_no_auto"; public static final String PREF_DO_NOT_DISTURB_START = "do_not_disturb_no_auto_start"; public static final String PREF_DO_NOT_DISTURB_END = "do_not_disturb_no_auto_end"; + public static final String PREF_FIND_PHONE = "prefs_find_phone"; + public static final String PREF_FIND_PHONE_DURATION = "prefs_find_phone_duration"; public static final UUID UUID_SERVICE = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index 105d929f4..2b6276b75 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -39,7 +39,6 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySampleDao; @@ -53,6 +52,9 @@ import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext; public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { + public static final int FindPhone_ON = -1; + public static final int FindPhone_OFF = 0; + private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3Coordinator.class); @@ -108,6 +110,37 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { } } + /** + * @return {@link #FindPhone_OFF}, {@link #FindPhone_ON}, or the duration + */ + public static int getFindPhone(SharedPreferences sharedPrefs) { + String findPhone = sharedPrefs.getString(MakibesHR3Constants.PREF_FIND_PHONE, getContext().getString(R.string.p_off)); + + if (findPhone.equals(getContext().getString(R.string.p_off))) { + return FindPhone_OFF; + } else if (findPhone.equals(getContext().getString(R.string.p_on))) { + return FindPhone_ON; + } else { // Duration + String duration = sharedPrefs.getString(MakibesHR3Constants.PREF_FIND_PHONE_DURATION, "0"); + + try { + int iDuration; + + try { + iDuration = Integer.valueOf(duration); + } catch (Exception ex) { + LOG.warn(ex.getMessage()); + iDuration = 60; + } + + return iDuration; + } catch (Exception e) { + LOG.error("Unexpected exception in MiBand2Coordinator.getTime: " + e.getMessage()); + return FindPhone_ON; + } + } + } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { @@ -225,7 +258,8 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { R.xml.devicesettings_timeformat, R.xml.devicesettings_liftwrist_display, R.xml.devicesettings_disconnectnotification, - R.xml.devicesettings_donotdisturb_no_auto + R.xml.devicesettings_donotdisturb_no_auto, + R.xml.devicesettings_find_phone }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java index e06cc605d..bc6830e25 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/makibeshr3/MakibesHR3DeviceSupport.java @@ -17,15 +17,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; -import android.os.Build; import android.os.CountDownTimer; import android.os.Handler; -import android.os.VibrationEffect; -import android.os.Vibrator; import android.widget.Toast; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -47,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants; import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Coordinator; @@ -77,9 +74,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3DeviceSupport.class); - private Handler mVibrationHandler = new Handler(); - private Vibrator mVibrator; - // The delay must be at least as long as it takes the watch to respond. // Reordering the requests could maybe reduce the delay, but this works fine too. private CountDownTimer mFetchCountDown = new CountDownTimer(2000, 2000) { @@ -95,6 +89,8 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } }; + private Handler mFindPhoneHandler = new Handler(); + private BluetoothGattCharacteristic mControlCharacteristic = null; private BluetoothGattCharacteristic mReportCharacteristic = null; @@ -413,35 +409,35 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement } private void onReverseFindDevice(boolean start) { - if (this.mVibrator.hasVibrator()) { - final long[] PATTERN = new long[]{ - 100, 100, - 100, 100, - 100, 100, - 500 - }; + if (start) { + SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs( + this.getDevice().getAddress()); - if (start) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - this.mVibrator.vibrate(VibrationEffect.createWaveform(PATTERN, 0)); - } else { - this.mVibrator.vibrate(PATTERN, 0); + int findPhone = MakibesHR3Coordinator.getFindPhone(sharedPreferences); + + if (findPhone != MakibesHR3Coordinator.FindPhone_OFF) { + GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone(); + + findPhoneEvent.event = GBDeviceEventFindPhone.Event.START; + + evaluateGBDeviceEvent(findPhoneEvent); + + if (findPhone > 0) { + this.mFindPhoneHandler.postDelayed(new Runnable() { + @Override + public void run() { + onReverseFindDevice(false); + } + }, findPhone * 1000); } - - // In case the connection is closed while we're searching for the device. - - this.mVibrationHandler.postDelayed(new Runnable() { - @Override - public void run() { - mVibrator.cancel(); - } - }, 1100 * 6); - - } else { - this.mVibrator.cancel(); } } else { - // TODO: Alternative handling. Don't use sound, the connection isn't secure. + // Always send stop, ignore preferences. + GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone(); + + findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP; + + evaluateGBDeviceEvent(findPhoneEvent); } } @@ -555,6 +551,9 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement key.equals(MakibesHR3Constants.PREF_DO_NOT_DISTURB_START) || key.equals(MakibesHR3Constants.PREF_DO_NOT_DISTURB_END)) { this.setQuiteHours(transactionBuilder, sharedPreferences); + } else if (key.equals(MakibesHR3Constants.PREF_FIND_PHONE) || + key.equals(MakibesHR3Constants.PREF_FIND_PHONE_DURATION)) { + // No action, we check the shared preferences when the device tries to ring the phone. } else { return; } @@ -594,8 +593,6 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement this.mControlCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_CONTROL); this.mReportCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT); - this.mVibrator = (Vibrator) this.getContext().getSystemService(Context.VIBRATOR_SERVICE); - builder.notify(this.mReportCharacteristic, true); builder.setGattCallback(this); diff --git a/app/src/main/res/drawable/ic_find_lost_phone.xml b/app/src/main/res/drawable/ic_find_lost_phone.xml new file mode 100644 index 000000000..2740ef687 --- /dev/null +++ b/app/src/main/res/drawable/ic_find_lost_phone.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 13f301202..6a38a6e9b 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -141,6 +141,18 @@ @string/p_pebble_privacy_mode_complete + + @string/off + @string/on + @string/maximum_duration + + + + @string/p_off + @string/p_on + @string/p_scheduled + + @string/dateformat_time @string/dateformat_date_time diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index af3b3a1c8..bde362b57 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -445,6 +445,10 @@ Alarms to reserve for upcoming events Use heart rate sensor to improve sleep detection Device time offset in hours (for detecting sleep of shift workers) + Find phone + Enable find phone + Use your band to play your phone\'s ringtone. + Ring duration in seconds Date format Time @@ -600,6 +604,7 @@ At sunset Automatic (sleep detection) Scheduled (time interval) + Duration Attempting to pair with %1$s Bonding with %1$s failed immediately. Trying to connect to: %1$s diff --git a/app/src/main/res/xml/devicesettings_find_phone.xml b/app/src/main/res/xml/devicesettings_find_phone.xml new file mode 100644 index 000000000..5baec8c70 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_find_phone.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file From 61ef4ea3ef70cea97e00961d409fb609c76b01bd Mon Sep 17 00:00:00 2001 From: Cre3per Date: Thu, 10 Oct 2019 11:34:29 +0200 Subject: [PATCH 18/34] adjusted changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b376aaf5a..cffec56d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Makibes HR3: Implemented device deletion, heart rate, steps, download progress notification, smart wake up, disconnect notification, quite hours * Makibes HR3: Fixed medium-length notifications not being displayed * Makibes HR3: Added a timeout to reverse find device +* "Find Phone" now vibrates in addition to playing the ring tone #### Version 0.36.3 * Basic Makibes HR3 support From 1d3cff029e9b80c363f335d56d8ec660d7e6c213 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 12 Oct 2019 12:53:35 +0200 Subject: [PATCH 19/34] clean up changelog (there was no 0.36.3 release) --- CHANGELOG.md | 18 +++++++++--------- app/build.gradle | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cffec56d8..8d48e6b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,14 @@ ### Changelog -#### Version 0.36.4 -* Makibes HR3: Adjusted alarm slot count -* Makibes HR3: Implemented device deletion, heart rate, steps, download progress notification, smart wake up, disconnect notification, quite hours -* Makibes HR3: Fixed medium-length notifications not being displayed -* Makibes HR3: Added a timeout to reverse find device -* "Find Phone" now vibrates in addition to playing the ring tone - -#### Version 0.36.3 -* Basic Makibes HR3 support +#### Version 0.37.0 +* Initial Basic Makibes HR3 support +* Amazfit Bip Lite: Iniital working support, firmware update is disabled for now (we do not have any firmware for testing) +* Find Phone now also vibration in addition to playing the ring tone +* ID115: All settings are now per-device +* Timeformat settings are now per-device for all supported devices +* Wrist location settings are now per-device for all supported devices +* Work around broken layout in database management activity +* Show toast in case no app is installed which can handle GPX files #### Version 0.36.2 * Amazfit Bip: Untested support for Lite variant diff --git a/app/build.gradle b/app/build.gradle index 56233071d..f20bebb8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { targetSdkVersion 27 // Note: always bump BOTH versionCode and versionName! - versionName "0.36.4" - versionCode 159 + versionName "0.37.0" + versionCode 158 vectorDrawables.useSupportLibrary = true } buildTypes { From 80c902e568149ee1321de82243c43f6fae2efd4d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 12 Oct 2019 13:04:33 +0200 Subject: [PATCH 20/34] Amazfit Cor 2: Enable Emoji Font setting and 3rd party HR access --- CHANGELOG.md | 1 + .../devices/huami/amazfitcor2/AmazfitCor2Coordinator.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d48e6b3c..0dab18820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Version 0.37.0 * Initial Basic Makibes HR3 support * Amazfit Bip Lite: Iniital working support, firmware update is disabled for now (we do not have any firmware for testing) +* Amazfit Cor 2: Enable Emoji Font setting and 3rd party HR access * Find Phone now also vibration in addition to playing the ring tone * ID115: All settings are now per-device * Timeformat settings are now per-device for all supported devices diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java index eab4dce79..3f59b5287 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java @@ -87,8 +87,11 @@ public class AmazfitCor2Coordinator extends HuamiCoordinator { return new int[]{ R.xml.devicesettings_amazfitcor, R.xml.devicesettings_wearlocation, + R.xml.devicesettings_custom_emoji_font, R.xml.devicesettings_liftwrist_display, R.xml.devicesettings_disconnectnotification, - R.xml.devicesettings_pairingkey}; + R.xml.devicesettings_expose_hr_thirdparty, + R.xml.devicesettings_pairingkey + }; } } From 533628c7323c6eedd77b1afcb15e489d1b4742c7 Mon Sep 17 00:00:00 2001 From: Full Name Date: Fri, 4 Oct 2019 18:38:47 +0000 Subject: [PATCH 21/34] Translated using Weblate (Czech) Currently translated at 100.0% (712 of 712 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/cs/ --- app/src/main/res/values-cs/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index ae9d69f43..0a2b70efa 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -747,4 +747,12 @@ Tlačítko Připojit nové zařízení Vždy viditelné Viditelné pouze pokud není přidáno žádné zařízení + + %d hodina + %d hodiny + %d hodin + + Pro zobrazení trasy aktivity nainstalujte aplikaci, která umí zobrazit GPX soubory. + Nastavení Makibes HR3 + Makibes HR3 \ No newline at end of file From 76025214e01007452a289cc4304f5b7a0e002ad9 Mon Sep 17 00:00:00 2001 From: naofum Date: Mon, 7 Oct 2019 09:35:58 +0000 Subject: [PATCH 22/34] Translated using Weblate (Japanese) Currently translated at 70.3% (495 of 704 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/ja/ --- app/src/main/res/values-ja/strings.xml | 51 +++++++++++++++++--------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e6775eefb..379394634 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -17,7 +17,7 @@ ナビゲーションドロワーを閉じる カードを長押しすると切断します 切断中 - 接続中 + 接続中… デバイスのスクリーンショットを取得中 デバッグ @@ -35,7 +35,7 @@ HRM を非アクティベート システムの天気アプリを有効にする システムの天気アプリを無効にする - 天気予報アプリをインストール + 天気予報通知アプリをインストール 設定 先頭に移動 @@ -44,17 +44,17 @@ ブラックリストにしたカレンダー ファームウェア・アプリのインストール - お使いのMi Bandに、現在のファームウェアの代わりに%sをインストールしようとしています。 + %s をインストールしようとしています。 お使いのMi Bandに、現在のファームウェアの代わりに %1$s および %2$s をインストールしようとしています。 このファームウェアはテスト済で、ガジェットブリッジと互換性があることがわかっています。 - このファームウェアは未テストで、Gadgetbridge と互換性がない可能性があります。 -\n -\nお使いのMi Bandにフラッシュすることは推奨されません! - それでも続行して、正しく動作した場合は、Gadgetbridge開発者にホワイトリストの %s ファームウェア バージョンを教えてください + このファームウェアは未テストで、Gadgetbridge と互換性がない可能性があります。 +\n +\nフラッシュすることは推奨されません! + それでも続行して、正しく動作した場合は、Gadgetbridge開発者にホワイトリストの %s ファームウェア バージョンを教えてください。 設定 全般設定 - Bluetoothがオンになったときにデバイスに接続 + Bluetoothがオンになったときに Gadgetbridge デバイスに接続 自動的に開始 自動的に再接続 お好みのオーディオプレイヤー @@ -427,14 +427,14 @@ 電話で開く ミュート 返信 - 接続 + 接続… Amazfit Cor に %s のファームウェアをインストールしようとしています。 \n -\n.fw ファイルをインストールし、その後 .res ファイルをインストールしください。お使いのウォッチは、.fw ファイルをインストールした後に再起動します。 +\n.fw ファイルをインストールし、その後 .res ファイルをインストールしください。お使いの band は、.fw ファイルをインストールした後に再起動します。 \n \n注: 以前にインストールされたものと同じ場合は、.res をインストールする必要はありません。 \n -\nテストされていません。デバイスが文鎮化する可能性があります。ご自身の責任で行って下さい! +\nご自身の責任で行って下さい! バックグラウンド JS を有効にします 有効にすると、ウォッチフェースに天気、バッテリー情報等を表示することができます。 Web View アクティビティ @@ -520,13 +520,13 @@ 工場出荷値にリセットすると、(サポートされている場合) 接続されているデバイスからすべてのデータを削除します。Xiaomi/Huamiデバイスでは、BluetoothのMACアドレスも変更するので、GadgetBligeに新しいデバイスとして表示されます。 すべて通知のブラックリストにする すべて通知のホワイトリストにする - Mi Band 3 に %s ファームウェアをインストールします。 -\n -\n.fw ファイルをインストールして、その後 .res ファイルをインストールするようにしてください。 .fw ファイルをインストールした後、ウォッチは再起動します。 -\n -\n注意: .res が以前にインストールしたものと全く同じ場合は、インストールする必要はありません。 -\n -\nテストされていません。お使いのデバイスが文鎮化する可能性があります。ご自身の責任で行ってください! + Mi Band 3 に %s ファームウェアをインストールします。 +\n +\n.fw ファイルをインストールして、その後 .res ファイルをインストールするようにしてください。 .fw ファイルをインストールした後、お使いの band は再起動します。 +\n +\n注意: .res が以前にインストールしたものと全く同じ場合は、インストールする必要はありません。 +\n +\nご自身の責任で行ってください! 通知の間の最小時間 右から左へ お使いのデバイスが右から左の言語を表示できない場合はこれを有効にします @@ -627,4 +627,19 @@ \nあなた自身のリスクで続行してください! \n  \n完全にはテストされていません。お使いのデバイス名が \"Amazfit Band 2\" の場合、おそらく BEATS_W ファームウェアをフラッシュする必要があります + VoIP アプリの通話を有効にする + Mi Band 4 に %s ファームウェアをインストールしようとしています。 +\n +\n.fw ファイルをインストールしてから、.res ファイルをインストールしてください。 .fw ファイルをインストールすると、お使いの band が再起動します。 +\n +\n注: 以前にインストールしたものとまったく同じ場合、.res をインストールする必要はありません。 +\n +\nご自身のリスクで進めてください! + Gadgetbridge の接続中に、他のアプリが HR データにリアルタイムでアクセスできるようにします + サードパーティのリアルタイムHRアクセス + カスタムフォントを使用する + お使いのデバイスで絵文字サポートのカスタムフォントファームウェアがある場合は、これを有効にしてください + 新しいデバイスを接続するボタン + 常に表示 + デバイスが追加されていない場合にのみ表示 \ No newline at end of file From 1fb65dde26e009ac8b30ce7ab0d83f28a33d10a0 Mon Sep 17 00:00:00 2001 From: Yaron Shahrabani Date: Fri, 11 Oct 2019 19:28:44 +0000 Subject: [PATCH 23/34] Translated using Weblate (Hebrew) Currently translated at 100.0% (705 of 705 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/he/ --- app/src/main/res/values-he/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 748f75f9e..4fe310511 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -753,4 +753,5 @@ כדי לצפות בעקבות פעילות, עליך להתקין יישומון שיכול לטפל בקובצי GPX. הגדרות של Makibes HR3 Makibes HR3 + Amazfit Bip Lite \ No newline at end of file From de7142cf84b059b0997307b9d18d1628bbe9ec25 Mon Sep 17 00:00:00 2001 From: FransM Date: Sat, 12 Oct 2019 09:33:10 +0000 Subject: [PATCH 24/34] Translated using Weblate (Dutch) Currently translated at 100.0% (705 of 705 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/nl/ --- app/src/main/res/values-nl/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 56075dd5d..2fb2246c2 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -347,7 +347,7 @@ Tijd Tijd & datum Knoppen acties - Specificeer actie voor de Mi Band 2 knopdruk + Specificeer actie voor de Mi Band knopdruk Aantal knopdrukken Aantal knopdrukken nodig om een bericht te sturen Uit te sturen bericht @@ -754,4 +754,8 @@ Verbind nieuw apparaat knop Altijkd zichtbaar Alleen zichtbaar als er geen apparaat toegevoegd is + Om de route van de activiteit te zien: installeer een app die GPX bestanden kan tonen. + Makibes HR3 instellingen + Makibes HR3 + Amazfit Bip Lite \ No newline at end of file From f0808f72329a805f50fe956f41423dbb04bd87f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=B0=91=E4=B8=BE?= Date: Sat, 12 Oct 2019 08:56:33 +0000 Subject: [PATCH 25/34] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (705 of 705 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 06c105690..792645038 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -760,4 +760,5 @@ 若需要查看活动轨迹,请安装一个能查看 GPX 文件的应用。 Makibes HR3 设置 Makibes HR3 + 米动手表青春版 Lite \ No newline at end of file From 8fefd1b49eb21b1b7d96ef7e59e4e9f4ace48cb6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 12 Oct 2019 19:04:12 +0200 Subject: [PATCH 26/34] * Mi Band 4/Amazfit Bip Lite: Trim white spaces and new lines from auth key --- CHANGELOG.md | 1 + .../service/devices/huami/operations/InitOperation.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dab18820..3b6e831c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Wrist location settings are now per-device for all supported devices * Work around broken layout in database management activity * Show toast in case no app is installed which can handle GPX files +* Mi Band 4/Amazfit Bip Lite: Trim white spaces and new lines from auth key #### Version 0.36.2 * Amazfit Bip: Untested support for Lite variant diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java index b655a8527..974b72128 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java @@ -94,7 +94,7 @@ public class InitOperation extends AbstractBTLEOperation { String authKey = sharedPrefs.getString("authkey", null); if (authKey != null && !authKey.isEmpty()) { - byte[] srcBytes = authKey.getBytes(); + byte[] srcBytes = authKey.trim().getBytes(); if (authKey.length() == 34 && authKey.substring(0, 2).equals("0x")) { srcBytes = GB.hexStringToByteArray(authKey.substring(2)); } From 4bf37ea70f77f13ed2dc584a6b9b9d97b59a15f7 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 12 Oct 2019 19:44:45 +0200 Subject: [PATCH 27/34] Mi Band 4/Amazfit Bip Lite: Display a toast and do not try to pair if there was no auth key supplied --- CHANGELOG.md | 1 + .../gadgetbridge/activities/ConfigureAlarms.java | 1 + .../gadgetbridge/activities/DiscoveryActivity.java | 14 +++++++++++++- .../devices/AbstractDeviceCoordinator.java | 3 ++- .../gadgetbridge/devices/DeviceCoordinator.java | 9 ++++++--- .../devices/UnknownDeviceCoordinator.java | 2 ++ .../casiogb6900/CasioGB6900DeviceCoordinator.java | 2 +- .../devices/hplus/HPlusCoordinator.java | 2 +- .../amazfitbip/AmazfitBipLiteCoordinator.java | 5 +++++ .../devices/huami/miband4/MiBand4Coordinator.java | 5 +++++ .../devices/id115/ID115Coordinator.java | 2 +- .../devices/jyou/BFH16DeviceCoordinator.java | 3 ++- .../devices/jyou/TeclastH30Coordinator.java | 2 +- .../devices/makibeshr3/MakibesHR3Coordinator.java | 3 +-- .../mijia_lywsd02/MijiaLywsd02Coordinator.java | 2 +- .../miscale2/MiScale2DeviceCoordinator.java | 2 +- .../devices/no1f1/No1F1Coordinator.java | 2 +- .../devices/roidmi/RoidmiCoordinator.java | 3 ++- .../devices/watch9/Watch9DeviceCoordinator.java | 2 +- .../devices/zetime/ZeTimeCoordinator.java | 2 +- .../BluetoothPairingRequestReceiver.java | 2 +- .../huami/amazfitbip/AmazfitBipLiteSupport.java | 4 +--- app/src/main/res/values/strings.xml | 1 + 23 files changed, 52 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6e831c5..9635721e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Work around broken layout in database management activity * Show toast in case no app is installed which can handle GPX files * Mi Band 4/Amazfit Bip Lite: Trim white spaces and new lines from auth key +* Mi Band 4/Amazfit Bip Lite: Display a toast and do not try to pair if there was no auth key supplied #### Version 0.36.2 * Amazfit Bip: Untested support for Lite variant diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index 1183a67ce..6293cb03c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -80,6 +80,7 @@ public class ConfigureAlarms extends AbstractGBActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQ_CONFIGURE_ALARM) { avoidSendAlarmsToDevice = false; updateAlarmsFromDB(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index d29bacc70..f88c692cf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; @@ -616,6 +617,17 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView stopDiscovery(); DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate); LOG.info("Using device candidate " + deviceCandidate + " with coordinator: " + coordinator.getClass()); + + if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_REQUIRE_KEY) { + SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceCandidate.getMacAddress()); + + String authKey = sharedPrefs.getString("authkey", null); + if (authKey == null || authKey.isEmpty() || authKey.getBytes().length < 34 || !authKey.substring(0, 2).equals("0x")) { + GB.toast(DiscoveryActivity.this, getString(R.string.discovery_need_to_enter_authkey), Toast.LENGTH_LONG, GB.WARN); + return; + } + } + Class pairingActivity = coordinator.getPairingActivity(); if (pairingActivity != null) { Intent intent = new Intent(this, pairingActivity); @@ -623,7 +635,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView startActivity(intent); } else { GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate); - int bondingStyle = coordinator.getBondingStyle(device); + int bondingStyle = coordinator.getBondingStyle(); if (bondingStyle == DeviceCoordinator.BONDING_STYLE_NONE) { LOG.info("No bonding needed, according to coordinator, so connecting right away"); connectAndFinish(device); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index 6d8a030f3..46bd46344 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -136,7 +136,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { } @Override - public int getBondingStyle(GBDevice device) { + public int getBondingStyle() { return BONDING_STYLE_ASK; } @@ -159,6 +159,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { return false; } + @NonNull @Override public int[] getColorPresets() { return new int[0]; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index a9d7fd7e7..567fa5b8d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -29,7 +29,6 @@ import java.util.Collection; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import nodomain.freeyourgadget.gadgetbridge.GBException; -import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsFragment; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; @@ -63,6 +62,11 @@ public interface DeviceCoordinator { */ int BONDING_STYLE_ASK = 2; + /** + * A secret key has to be entered before connecting + */ + int BONDING_STYLE_REQUIRE_KEY = 3; + /** * Checks whether this coordinator handles the given candidate. * Returns the supported device type for the given candidate or @@ -224,9 +228,8 @@ public interface DeviceCoordinator { /** * Returns how/if the given device should be bonded before connecting to it. - * @param device */ - int getBondingStyle(GBDevice device); + int getBondingStyle(); /** * Indicates whether the device has some kind of calender we can sync to. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index df6fc0596..255cacdd7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -93,6 +93,7 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { sampleProvider = new UnknownSampleProvider(); } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { return DeviceType.UNKNOWN; @@ -197,6 +198,7 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { return false; } + @NonNull @Override public int[] getColorPresets() { return new int[0]; 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 index cb9dc6cad..3bd12815a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casiogb6900/CasioGB6900DeviceCoordinator.java @@ -58,7 +58,7 @@ public class CasioGB6900DeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate){ + public int getBondingStyle(){ return BONDING_STYLE_BOND; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 621ae54d2..b9e3326d5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -84,7 +84,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate){ + public int getBondingStyle(){ return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipLiteCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipLiteCoordinator.java index d25067b77..b2ce87a0b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipLiteCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipLiteCoordinator.java @@ -57,4 +57,9 @@ public class AmazfitBipLiteCoordinator extends AmazfitBipCoordinator { public InstallHandler findInstallHandler(Uri uri, Context context) { return null; } + + @Override + public int getBondingStyle() { + return BONDING_STYLE_REQUIRE_KEY; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java index 68ce12840..c451e8d49 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java @@ -97,4 +97,9 @@ public class MiBand4Coordinator extends HuamiCoordinator { R.xml.devicesettings_pairingkey }; } + + @Override + public int getBondingStyle() { + return BONDING_STYLE_REQUIRE_KEY; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/id115/ID115Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/id115/ID115Coordinator.java index f60532eaf..fb8717b6d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/id115/ID115Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/id115/ID115Coordinator.java @@ -66,7 +66,7 @@ public class ID115Coordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate){ + public int getBondingStyle(){ return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java index 020a38485..dd90e41a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java @@ -70,6 +70,7 @@ public class BFH16DeviceCoordinator extends AbstractDeviceCoordinator return Collections.singletonList(filter); } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { @@ -85,7 +86,7 @@ public class BFH16DeviceCoordinator extends AbstractDeviceCoordinator } @Override - public int getBondingStyle(GBDevice deviceCandidate){ + public int getBondingStyle(){ return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java index 4c994bf1f..68a464af4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java @@ -82,7 +82,7 @@ public class TeclastH30Coordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate){ + public int getBondingStyle(){ return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java index 2b6276b75..d17f535e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/makibeshr3/MakibesHR3Coordinator.java @@ -19,7 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.GregorianCalendar; import android.app.Activity; import android.content.Context; @@ -162,7 +161,7 @@ public class MakibesHR3Coordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate) { + public int getBondingStyle() { return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java index d0bcdee58..76e05fdba 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java @@ -49,7 +49,7 @@ public class MijiaLywsd02Coordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate) { + public int getBondingStyle() { return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java index c2308026f..c042e04d4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java @@ -85,7 +85,7 @@ public class MiScale2DeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice device) { + public int getBondingStyle() { return super.BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java index 4affefa09..79c5943a3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java @@ -71,7 +71,7 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate) { + public int getBondingStyle() { return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/roidmi/RoidmiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/roidmi/RoidmiCoordinator.java index 1aa75024d..574c2f4fe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/roidmi/RoidmiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/roidmi/RoidmiCoordinator.java @@ -44,7 +44,7 @@ public abstract class RoidmiCoordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice device) { + public int getBondingStyle() { return BONDING_STYLE_BOND; } @@ -133,6 +133,7 @@ public abstract class RoidmiCoordinator extends AbstractDeviceCoordinator { return true; } + @NonNull @Override public int[] getColorPresets() { return RoidmiConst.COLOR_PRESETS; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/watch9/Watch9DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/watch9/Watch9DeviceCoordinator.java index 878076f94..18b0a8844 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/watch9/Watch9DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/watch9/Watch9DeviceCoordinator.java @@ -77,7 +77,7 @@ public class Watch9DeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice deviceCandidate) { + public int getBondingStyle() { return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeCoordinator.java index 3f80a8b53..3ddc4cdc3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimeCoordinator.java @@ -153,7 +153,7 @@ public class ZeTimeCoordinator extends AbstractDeviceCoordinator { } @Override - public int getBondingStyle(GBDevice device) { + public int getBondingStyle() { return BONDING_STYLE_NONE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java index c262b00fe..70d39648d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java @@ -61,7 +61,7 @@ public class BluetoothPairingRequestReceiver extends BroadcastReceiver { DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice); try { - if (coordinator.getBondingStyle(gbDevice) == DeviceCoordinator.BONDING_STYLE_NONE) { + if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_NONE) { LOG.info("Aborting unwanted pairing request"); abortBroadcast(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipLiteSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipLiteSupport.java index 4db0af32b..fb6c6bb61 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipLiteSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipLiteSupport.java @@ -20,8 +20,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; import android.content.Context; import android.net.Uri; -import java.io.IOException; - import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; public class AmazfitBipLiteSupport extends AmazfitBipSupport { @@ -37,7 +35,7 @@ public class AmazfitBipLiteSupport extends AmazfitBipSupport { } @Override - public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { + public HuamiFWHelper createFWHelper(Uri uri, Context context) { return null; } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bde362b57..bbe2a38f4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -300,6 +300,7 @@ When your Mi Band vibrates and blinks, tap it a few times in a row. Install Make your device discoverable. Currently connected devices will likely not be discovered. Activate location (e.g. GPS) on Android 6+. Disable Privacy Guard for Gadgetbridge, because it may crash and reboot your phone. If no device is found after a few minutes, try again after rebooting your mobile device. + This device needs a secret auth key, long press on the device to enter it. Read the wiki. Note: Device image Name/Alias From 7b5e3336013ee197d84e660cf0f23bc9688a13aa Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 12 Oct 2019 20:15:09 +0200 Subject: [PATCH 28/34] Skip service scan if supported device could be recognized without uuids during discovery --- CHANGELOG.md | 1 + .../activities/DiscoveryActivity.java | 15 ++++++++---- .../huami/miband2/MiBand2Coordinator.java | 5 ---- .../gadgetbridge/util/DeviceHelper.java | 23 +++++++++---------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9635721e9..d14c7b8cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Show toast in case no app is installed which can handle GPX files * Mi Band 4/Amazfit Bip Lite: Trim white spaces and new lines from auth key * Mi Band 4/Amazfit Bip Lite: Display a toast and do not try to pair if there was no auth key supplied +* Skip service scan if supported device could be recognized without uuids during discovery #### Version 0.36.2 * Amazfit Bip: Untested support for Lite variant diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index f88c692cf..f197af416 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -196,8 +196,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { - LOG.warn(device.getName() + ": " + ((scanRecord != null) ? scanRecord.length : -1)); - logMessageContent(scanRecord); + //logMessageContent(scanRecord); handleDeviceFound(device, (short) rssi); } }; @@ -339,6 +338,12 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView } private void handleDeviceFound(BluetoothDevice device, short rssi) { + if (device.getName() != null) { + if (handleDeviceFound(device,rssi, null)) { + LOG.info("found supported device " + device.getName() + " without scanning services, skipping service scan."); + return; + } + } ParcelUuid[] uuids = device.getUuids(); if (uuids == null) { if (device.fetchUuidsWithSdp()) { @@ -350,7 +355,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView } - private void handleDeviceFound(BluetoothDevice device, short rssi, ParcelUuid[] uuids) { + private boolean handleDeviceFound(BluetoothDevice device, short rssi, ParcelUuid[] uuids) { LOG.debug("found device: " + device.getName() + ", " + device.getAddress()); if (LOG.isDebugEnabled()) { if (uuids != null && uuids.length > 0) { @@ -360,7 +365,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView } } if (device.getBondState() == BluetoothDevice.BOND_BONDED) { - return; // ignore already bonded devices + return true; // ignore already bonded devices } GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi, uuids); @@ -375,7 +380,9 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView deviceCandidates.add(candidate); } cadidateListAdapter.notifyDataSetChanged(); + return true; } + return false; } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java index 52d22230a..b087dbe1d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java @@ -46,11 +46,6 @@ public class MiBand2Coordinator extends HuamiCoordinator { @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { - if (candidate.supportsService(HuamiService.UUID_SERVICE_MIBAND2_SERVICE)) { - return DeviceType.MIBAND2; - } - - // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); String name = device.getName(); 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 baada67ff..e1af93727 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -82,14 +82,13 @@ public class DeviceHelper { private static final Logger LOG = LoggerFactory.getLogger(DeviceHelper.class); private static final DeviceHelper instance = new DeviceHelper(); + // lazily created + private List coordinators; public static DeviceHelper getInstance() { return instance; } - // lazily created - private List coordinators; - public DeviceType getSupportedType(GBDeviceCandidate candidate) { for (DeviceCoordinator coordinator : getAllCoordinators()) { DeviceType deviceType = coordinator.getSupportedType(candidate); @@ -203,15 +202,15 @@ public class DeviceHelper { private List createCoordinators() { List result = new ArrayList<>(); - result.add(new MiScale2DeviceCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new AmazfitBipCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new AmazfitBipLiteCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new AmazfitCorCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new AmazfitCor2Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new MiBand3Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new MiBand4Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new MiBand2HRXCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm - result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm + result.add(new MiScale2DeviceCoordinator()); + result.add(new AmazfitBipCoordinator()); + result.add(new AmazfitBipLiteCoordinator()); + result.add(new AmazfitCorCoordinator()); + result.add(new AmazfitCor2Coordinator()); + result.add(new MiBand3Coordinator()); + result.add(new MiBand4Coordinator()); + result.add(new MiBand2HRXCoordinator()); + result.add(new MiBand2Coordinator()); // Note: MiBand2 and all of the above must come before MiBand because detection is hacky, atm result.add(new MiBandCoordinator()); result.add(new PebbleCoordinator()); result.add(new VibratissimoCoordinator()); From 8189c03b089aa11c8609afe1eda1a52b9fa8afac Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 12 Oct 2019 20:41:27 +0200 Subject: [PATCH 29/34] update changelogs and readme --- CHANGELOG.md | 6 +++--- README.md | 20 ++++++++++--------- app/src/main/res/xml/changelog_master.xml | 14 +++++++++++++ .../metadata/android/en-US/changelogs/158.txt | 12 +++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/158.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index d14c7b8cf..748b070ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ ### Changelog #### Version 0.37.0 -* Initial Basic Makibes HR3 support -* Amazfit Bip Lite: Iniital working support, firmware update is disabled for now (we do not have any firmware for testing) +* Initial Makibes HR3 support +* Amazfit Bip Lite: Inittal working support, firmware update is disabled for now (we do not have any firmware for testing) * Amazfit Cor 2: Enable Emoji Font setting and 3rd party HR access * Find Phone now also vibration in addition to playing the ring tone * ID115: All settings are now per-device -* Timeformat settings are now per-device for all supported devices +* Time format settings are now per-device for all supported devices * Wrist location settings are now per-device for all supported devices * Work around broken layout in database management activity * Show toast in case no app is installed which can handle GPX files diff --git a/README.md b/README.md index 1513a9c47..e61fd2070 100644 --- a/README.md +++ b/README.md @@ -28,28 +28,30 @@ vendor's servers. [List of changes](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/master/CHANGELOG.md) -## Supported Devices +## Supported Devices (Some of them WIP and some of them without maintainer) * Amazfit Bip [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip) +* Amazfit Bip Lite (NOT RECOMMENDED, NEEDS MI FIT WITH ACCOUNT AND ROOT ONCE) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-Lite) * Amazfit Cor [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor) * Amazfit Cor 2 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor-2) * BFH-16 -* Casio GB-6900B (WIP) +* Casio GB-6900B * HPlus Devices (e.g. ZeBand) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/HPlus) -* ID115 (WIP) -* Lenovo Watch 9 (WIP) -* Liveview (WIP) +* ID115 +* Lenovo Watch 9 +* Liveview +* Makibes HR3 * Mi Band, Mi Band 1A, Mi Band 1S [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band) * Mi Band 2 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-2) * Mi Band 3 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-3) -* Mi Band 4 (WIP, NOT RECOMMENDED, NEEDS MI FIT WITH ACCOUNT AND ROOT ONCE) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-4) +* Mi Band 4 (NOT RECOMMENDED, NEEDS MI FIT WITH ACCOUNT AND ROOT ONCE) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-4) * Mi Scale 2 (currently only displays a toast after stepping on the scale) -* NO.1 F1 (WIP) +* NO.1 F1 * Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Pebble) * Pebble 2 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Pebble) -* Teclast H10, H30 (WIP) +* Teclast H10, H30 * XWatch (Affordable Chinese Casio-like smartwatches) * Vibratissimo (experimental) -* ZeTime (WIP) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/MyKronoz-ZeTime) +* ZeTime [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/MyKronoz-ZeTime) ## Features diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index e5d36351c..de4646768 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,19 @@ + + Initial Makibes HR3 support + Amazfit Bip Lite: Inittal working support, firmware update is disabled for now (we do not have any firmware for testing) + Amazfit Cor 2: Enable Emoji Font setting and 3rd party HR access + Find Phone now also vibration in addition to playing the ring tone + ID115: All settings are now per-device + Time format settings are now per-device for all supported devices + Wrist location settings are now per-device for all supported devices + Work around broken layout in database management activity + Show toast in case no app is installed which can handle GPX files + Mi Band 4/Amazfit Bip Lite: Trim white spaces and new lines from auth key + Mi Band 4/Amazfit Bip Lite: Display a toast and do not try to pair if there was no auth key supplied + Skip service scan if supported device could be recognized without uuids during discovery + Amazfit Bip: Untested support for Lite variant Force Lineage OS to ask for permission when Trust is used to fix non-working incoming calls diff --git a/fastlane/metadata/android/en-US/changelogs/158.txt b/fastlane/metadata/android/en-US/changelogs/158.txt new file mode 100644 index 000000000..ea026740a --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/158.txt @@ -0,0 +1,12 @@ +* Initial Makibes HR3 support +* Amazfit Bip Lite: Inittal working support, firmware update is disabled for now (we do not have any firmware for testing) +* Amazfit Cor 2: Enable Emoji Font setting and 3rd party HR access +* Find Phone now also vibration in addition to playing the ring tone +* ID115: All settings are now per-device +* Time format settings are now per-device for all supported devices +* Wrist location settings are now per-device for all supported devices +* Work around broken layout in database management activity +* Show toast in case no app is installed which can handle GPX files +* Mi Band 4/Amazfit Bip Lite: Trim white spaces and new lines from auth key +* Mi Band 4/Amazfit Bip Lite: Display a toast and do not try to pair if there was no auth key supplied +* Skip service scan if supported device could be recognized without uuids during discovery From 2334b6581a5b3647084c25b1924447d6e91bf4f1 Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Sat, 12 Oct 2019 11:13:39 +0000 Subject: [PATCH 30/34] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (710 of 710 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/pt_BR/ --- app/src/main/res/values-pt-rBR/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 23e7c456c..3cf8f2bce 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -763,4 +763,10 @@ Para visualizar o rastreamento de atividade, instale um aplicativo que consegue manipular arquivos GPX. Configurações de Makibes HR3 Makibes HR3 + Amazfit Bip Lite + Encontrar telefone + Ativar encontrar telefone + Use sua pulseira para reproduzir o toque sonoro do seu celular. + Duração do toque sonoro em segundos + Duração \ No newline at end of file From 0ab14f0bafd1c1aff5cab13c870fe57a22a8cb9b Mon Sep 17 00:00:00 2001 From: nautilusx Date: Sat, 12 Oct 2019 11:18:38 +0000 Subject: [PATCH 31/34] Translated using Weblate (German) Currently translated at 100.0% (710 of 710 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/de/ --- app/src/main/res/values-de/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e168184ac..e37211021 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -769,4 +769,10 @@ Makibes HR3 Einstellungen Makibes HR3 + Amazfit Bip Lite + Telefon finden + Telefon finden aktivieren + Verwende dein Band, um den Klingelton deines Handys wiederzugeben. + Klingeldauer in Sekunden + Dauer \ No newline at end of file From 3512154a309e68bd6153040d5cce60edf217d05b Mon Sep 17 00:00:00 2001 From: Full Name Date: Sat, 12 Oct 2019 11:20:43 +0000 Subject: [PATCH 32/34] Translated using Weblate (Czech) Currently translated at 100.0% (710 of 710 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/cs/ --- app/src/main/res/values-cs/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 0a2b70efa..0f4602f2d 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -755,4 +755,10 @@ Pro zobrazení trasy aktivity nainstalujte aplikaci, která umí zobrazit GPX soubory. Nastavení Makibes HR3 Makibes HR3 + Amazfit Bip Lite + Vyhledat telefon + Povolit vyhledání telefonu + Použijte náramek/hodinky k prozvonění telefonu. + Délka zvonění ve vteřinách + Délka \ No newline at end of file From fa95971dfd25d201ceff6bbf1bbf6b046cafaed3 Mon Sep 17 00:00:00 2001 From: Yaron Shahrabani Date: Sat, 12 Oct 2019 13:39:48 +0000 Subject: [PATCH 33/34] Translated using Weblate (Hebrew) Currently translated at 100.0% (710 of 710 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/he/ --- app/src/main/res/values-he/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 4fe310511..d60ff5c45 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -754,4 +754,9 @@ הגדרות של Makibes HR3 Makibes HR3 Amazfit Bip Lite + איתור הטלפון + הפעלת איתור הטלפון + ניתן להשתמש בצמיד כדי שהטלפון שלך ישמיע צלצול. + משך זמן הצלצול בשניות + משך \ No newline at end of file From 6a26a286f8fb7c76e78e996dafa56ed7e278c51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=B0=91=E4=B8=BE?= Date: Sat, 12 Oct 2019 11:12:18 +0000 Subject: [PATCH 34/34] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (710 of 710 strings) Translation: Freeyourgadget/Gadgetbridge Translate-URL: https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 792645038..3bf151cf2 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -761,4 +761,9 @@ Makibes HR3 设置 Makibes HR3 米动手表青春版 Lite + 查找手机 + 启用查找手机 + 使用您的手环以在手机上播放铃声。 + 铃声将持续数秒 + 持续 \ No newline at end of file