diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfaf57c5..5725e3bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ### Changelog +#### Version 0.21.1 +* Initial support for EXRIZU K8 (HPLus variant) +* Amazfit Bip: fix long messages not being displayed at all +* Mi Band 2: Support multiple button actions +* NO.1 F1: Fetch sleep data +* NO.1 F1: Heart rate support +* Pebble: Support controlling the current active media playback application +* Fix suspended activities coming to front when rotating the screen + #### Version 0.21.0 * Initial NO.1 F1 support * Initial Teclast H30 support diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index b4ad0dc7f..1f9c7bdbc 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -42,7 +42,7 @@ public class GBDaoGenerator { public static void main(String[] args) throws Exception { - Schema schema = new Schema(16, MAIN_PACKAGE + ".entities"); + Schema schema = new Schema(17, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -265,6 +265,7 @@ public class GBDaoGenerator { activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); return activitySample; } diff --git a/app/build.gradle b/app/build.gradle index a9dff3d8a..c0e7d68c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 25 // note: always bump BOTH versionCode and versionName! - versionName "0.21.0" - versionCode 101 + versionName "0.21.1" + versionCode 102 vectorDrawables.useSupportLibrary = true } buildTypes { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java index 475031e23..c0d5a0092 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java @@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; public abstract class AbstractGBActivity extends AppCompatActivity implements GBActivity { + private boolean isLanguageInvalid = false; public static final int NONE = 0; public static final int NO_ACTIONBAR = 1; @@ -52,8 +53,11 @@ public abstract class AbstractGBActivity extends AppCompatActivity implements GB } }; - public void setLanguage(Locale language, boolean recreate) { - AndroidUtils.setLanguage(this, language, recreate); + public void setLanguage(Locale language, boolean invalidateLanguage) { + if (invalidateLanguage) { + isLanguageInvalid = true; + } + AndroidUtils.setLanguage(this, language); } public static void init(GBActivity activity) { @@ -88,6 +92,15 @@ public abstract class AbstractGBActivity extends AppCompatActivity implements GB super.onCreate(savedInstanceState); } + @Override + protected void onResume() { + super.onResume(); + if (isLanguageInvalid) { + isLanguageInvalid = false; + recreate(); + } + } + @Override protected void onDestroy() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index a1ca99131..1cdb0a158 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -49,6 +49,8 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi private static final Logger LOG = LoggerFactory.getLogger(AbstractSettingsActivity.class); + private boolean isLanguageInvalid = false; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -152,6 +154,15 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi } } + @Override + protected void onResume() { + super.onResume(); + if (isLanguageInvalid) { + isLanguageInvalid = false; + recreate(); + } + } + @Override protected void onDestroy() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); @@ -210,7 +221,10 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi return super.onOptionsItemSelected(item); } - public void setLanguage(Locale language, boolean recreate) { - AndroidUtils.setLanguage(this, language, recreate); + public void setLanguage(Locale language, boolean invalidateLanguage) { + if (invalidateLanguage) { + isLanguageInvalid = true; + } + AndroidUtils.setLanguage(this, language); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java index 54e4ae15e..88a6b2a20 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Daniele Gobbetti +/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java index 377663d83..171ee0159 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java @@ -78,6 +78,8 @@ public class ControlCenterv2 extends AppCompatActivity private GBDeviceAdapterv2 mGBDeviceAdapter; private RecyclerView deviceListView; + private boolean isLanguageInvalid = false; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -206,6 +208,15 @@ public class ControlCenterv2 extends AppCompatActivity } } + @Override + protected void onResume() { + super.onResume(); + if (isLanguageInvalid) { + isLanguageInvalid = false; + recreate(); + } + } + @Override protected void onDestroy() { unregisterForContextMenu(deviceListView); @@ -311,7 +322,10 @@ public class ControlCenterv2 extends AppCompatActivity ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0); } - public void setLanguage(Locale language, boolean recreate) { - AndroidUtils.setLanguage(this, language, recreate); + public void setLanguage(Locale language, boolean invalidateLanguage) { + if (invalidateLanguage) { + isLanguageInvalid = true; + } + AndroidUtils.setLanguage(this, language); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java index 7600c8f66..13a5d640c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java @@ -1,9 +1,25 @@ +/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo + + 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.activities; import java.util.Locale; public interface GBActivity { - void setLanguage(Locale language, boolean recreate); + void setLanguage(Locale language, boolean invalidateLanguage); void setTheme(int themeId); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 94419a15b..16cfe61e0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -159,6 +159,7 @@ public class SettingsActivity extends AbstractSettingsActivity { String newLang = newVal.toString(); try { GBApplication.setLanguage(newLang); + recreate(); } catch (Exception ex) { GB.toast(getApplicationContext(), "Error setting language: " + ex.getLocalizedMessage(), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java new file mode 100644 index 000000000..04c948539 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java @@ -0,0 +1,38 @@ +/* Copyright (C) 2017 protomors + + 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.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySampleDao; + +public class GadgetbridgeUpdate_17 implements DBUpdateScript { + @Override + public void upgradeSchema(SQLiteDatabase db) { + if (!DBHelper.existsColumn(No1F1ActivitySampleDao.TABLENAME, No1F1ActivitySampleDao.Properties.HeartRate.columnName, db)) { + String ADD_COLUMN_HEART_RATE = "ALTER TABLE " + No1F1ActivitySampleDao.TABLENAME + " ADD COLUMN " + + No1F1ActivitySampleDao.Properties.HeartRate.columnName + " INTEGER NOT NULL DEFAULT 0;"; + db.execSQL(ADD_COLUMN_HEART_RATE); + } + } + + @Override + public void downgradeSchema(SQLiteDatabase db) { + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java new file mode 100644 index 000000000..a630bb92e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java @@ -0,0 +1,55 @@ +/* Copyright (C) 2017 João Paulo Barraca, Quallenauge + + 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.hplus; + +/* +* @author Quallenauge <Hamsi2k@freenet.de> +*/ + + +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +/** + * Pseudo Coordinator for the EXRIZU K8, a sub type of the HPLUS devices + */ +public class EXRIZUK8Coordinator extends HPlusCoordinator { + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + if(name != null && name.startsWith("iRun ")){ + return DeviceType.EXRIZUK8; + } + + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.EXRIZUK8; + } + + @Override + public String getManufacturer() { + return "EXRIZU"; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java index 1d87f181e..8476fba57 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017 Sami Alaoui + + 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.jyou; import java.util.UUID; 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 e170b64c2..993f425e6 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 @@ -1,3 +1,20 @@ +/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, protomors, Sami Alaoui + + 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.jyou; import android.annotation.TargetApi; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index 6fc1ce244..3c4561c4f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -207,6 +207,8 @@ public class MiBand2Service { public static final byte SUCCESS = 0x01; public static final byte COMMAND_ACTIVITY_DATA_START_DATE = 0x01; + public static final byte COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY = 0x01; + public static final byte COMMAND_ACTIVITY_DATA_TYPE_UNKNOWN_2 = 0x02; public static final byte COMMAND_ACTIVITY_DATA_XXX_DATE = 0x02; // issued on first connect, followd by COMMAND_XXXX_ACTIVITY_DATA instead of COMMAND_FETCH_ACTIVITY_DATA public static final byte COMMAND_FIRMWARE_INIT = 0x01; // to UUID_CHARACTERISTIC_FIRMWARE, followed by fw file size in bytes diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index bc0078d8a..077814665 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Christian - Fischer, Daniele Gobbetti, José Rebelo, Szymon Tomasz Stefanek + Fischer, Daniele Gobbetti, José Rebelo, Michal Novotny, Szymon Tomasz Stefanek This file is part of Gadgetbridge. @@ -36,6 +36,7 @@ public final class MiBandConst { public static final String PREF_MIBAND_BUTTON_ACTION_VIBRATE = "mi2_button_action_vibrate"; public static final String PREF_MIBAND_BUTTON_PRESS_COUNT = "mi_button_press_count"; public static final String PREF_MIBAND_BUTTON_PRESS_MAX_DELAY = "mi_button_press_count_max_delay"; + public static final String PREF_MIBAND_BUTTON_ACTION_DELAY = "mi_button_press_count_match_delay"; public static final String PREF_MIBAND_BUTTON_PRESS_BROADCAST = "mi_button_press_broadcast"; public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection"; public static final String PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS = "mi_device_time_offset_hours"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java index 651fdcb48..05049a2b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017 protomors + + 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.no1f1; import java.util.UUID; @@ -18,6 +34,8 @@ public final class No1F1Constants { public static final byte CMD_REALTIME_STEPS = (byte) 0xb1; public static final byte CMD_FETCH_STEPS = (byte) 0xb2; public static final byte CMD_FETCH_SLEEP = (byte) 0xb3; + public static final byte CMD_REALTIME_HEARTRATE = (byte) 0xe5; + public static final byte CMD_FETCH_HEARTRATE = (byte) 0xe6; public static final byte CMD_NOTIFICATION = (byte) 0xc1; public static final byte CMD_ICON = (byte) 0xc3; public static final byte CMD_DEVICE_SETTINGS = (byte) 0xd3; 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 4cd9dcb54..3ff03d642 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 @@ -1,3 +1,20 @@ +/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, protomors + + 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.no1f1; import android.annotation.TargetApi; @@ -108,7 +125,7 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator { @Override public boolean supportsHeartRateMeasurement(GBDevice device) { - return false; + return true; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java index e7a615fdb..2b7b1e5db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017 protomors + + 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.no1f1; import android.support.annotation.NonNull; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java index 349fac0c0..7e0d7b805 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti, Kevin Richter +/* Copyright (C) 2016-2017 Andreas Shimokawa, AnthonyDiGirolamo, Daniele + Gobbetti, Kevin Richter This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index e55c1065d..eca9e1691 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, João Paulo Barraca + Gobbetti, João Paulo Barraca, protomors, Quallenauge, Sami Alaoui This file is part of Gadgetbridge. @@ -37,6 +37,7 @@ public enum DeviceType { LIVEVIEW(30, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled), HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled), MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled), + EXRIZUK8(42, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled), NO1F1(50, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled), TECLASTH30(60, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java index 837dec1a9..228de8650 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Julien - Pivotto, Kevin Richter +/* Copyright (C) 2015-2017 Andreas Shimokawa, AnthonyDiGirolamo, Carsten + Pfeiffer, Julien Pivotto, Kevin Richter This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index d085de772..07b1f3b1a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -1,5 +1,6 @@ /* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, - Daniele Gobbetti, João Paulo Barraca, Sergey Trofimov + Daniele Gobbetti, João Paulo Barraca, protomors, Quallenauge, Sami Alaoui, + Sergey Trofimov This file is part of Gadgetbridge. @@ -127,6 +128,9 @@ public class DeviceSupportFactory { case MAKIBESF68: deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case EXRIZUK8: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.EXRIZUK8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case NO1F1: deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java index da5fe2dd5..107af38da 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCallback.java @@ -1,19 +1,3 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer - - 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 . */ /* * Copyright (C) 2013 The Android Open Source Project * diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java index 587c8da71..92514d71a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java index c9dc6fa4d..aec1c2bee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java @@ -45,9 +45,11 @@ public class AmazfitBipFirmwareInfo extends Mi2FirmwareInfo { static { // firmware crcToVersion.put(25257, "0.0.8.74"); + crcToVersion.put(57724, "0.0.8.88"); // resources crcToVersion.put(12586, "RES 0.0.8.74"); + crcToVersion.put(34068, "RES 0.0.8.88"); // gps crcToVersion.put(61520, "GPS 9367,8f79a91,0,0,"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java index 57fb1941e..af877879d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -80,7 +80,7 @@ public class AmazfitBipSupport extends MiBand2Support { try { TransactionBuilder builder = performInitialized("new notification"); AlertNotificationProfile profile = new AlertNotificationProfile(this); - profile.setMaxLength(255); // TODO: find out real limit, certainly it is more than 18 which is default + profile.setMaxLength(230); int customIconId = AmazfitBipIcon.mapToIconId(notificationSpec.type); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java index 48317d550..053c81097 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index dee5805a6..436efcf01 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -1,5 +1,5 @@ /* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer, - ivanovlev, João Paulo Barraca, Pavel Motyrev + ivanovlev, João Paulo Barraca, Pavel Motyrev, Quallenauge This file is part of Gadgetbridge. @@ -160,7 +160,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport syncPreferences(TransactionBuilder transaction) { - if (deviceType == DeviceType.HPLUS) { + if (deviceType == DeviceType.HPLUS || deviceType == DeviceType.EXRIZUK8) { setSIT(transaction); //Sync SIT Interval } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java index 8b1f09a84..f30d6bd5e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017 Sami Alaoui + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.jyou; import android.bluetooth.BluetoothGatt; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index d1ac3c331..e3c0d883a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -1,6 +1,6 @@ /* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Christian Fischer, Daniele Gobbetti, JohnnySun, José Rebelo, Julien Pivotto, Kasha, - Sergey Trofimov, Steffen Liebergeld + Michal Novotny, Sergey Trofimov, Steffen Liebergeld This file is part of Gadgetbridge. @@ -24,9 +24,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.net.Uri; -import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.text.format.DateFormat; import android.widget.Toast; @@ -42,6 +40,8 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -126,8 +126,10 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ge public class MiBand2Support extends AbstractBTLEDeviceSupport { // We introduce key press counter for notification purposes + private static int currentButtonActionId = 0; private static int currentButtonPressCount = 0; private static long currentButtonPressTime = 0; + private static long currentButtonTimerActivationTime = 0; private static final Logger LOG = LoggerFactory.getLogger(MiBand2Support.class); private final DeviceInfoProfile deviceInfoProfile; @@ -782,6 +784,80 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // not supported } + public void runButtonAction() { + Prefs prefs = GBApplication.getPrefs(); + + if (currentButtonTimerActivationTime != currentButtonPressTime) { + return; + } + + String requiredButtonPressMessage = prefs.getString(MiBandConst.PREF_MIBAND_BUTTON_PRESS_BROADCAST, + this.getContext().getString(R.string.mi2_prefs_button_press_broadcast_default_value)); + + Intent in = new Intent(); + in.setAction(requiredButtonPressMessage); + in.putExtra("button_id", currentButtonActionId); + LOG.info("Sending " + requiredButtonPressMessage + " with button_id " + currentButtonActionId); + this.getContext().getApplicationContext().sendBroadcast(in); + if (prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_VIBRATE, false)) { + performPreferredNotification(null, null, null, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, null); + } + + currentButtonActionId = 0; + + currentButtonPressCount = 0; + currentButtonPressTime = System.currentTimeMillis(); + } + + public void handleButtonPressed(byte[] value) { + LOG.info("Button pressed"); + ///logMessageContent(value); + + // If disabled we return from function immediately + Prefs prefs = GBApplication.getPrefs(); + if (!prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_ENABLE, false)) { + return; + } + + int buttonPressMaxDelay = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_MAX_DELAY, 2000); + int buttonActionDelay = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_ACTION_DELAY, 0); + int requiredButtonPressCount = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_COUNT, 0); + + if (requiredButtonPressCount > 0) { + long timeSinceLastPress = System.currentTimeMillis() - currentButtonPressTime; + + if ((currentButtonPressTime == 0) || (timeSinceLastPress < buttonPressMaxDelay)) { + currentButtonPressCount++; + } + else { + currentButtonPressCount = 1; + currentButtonActionId = 0; + } + + currentButtonPressTime = System.currentTimeMillis(); + if (currentButtonPressCount == requiredButtonPressCount) { + currentButtonTimerActivationTime = currentButtonPressTime; + if (buttonActionDelay > 0) { + LOG.info("Activating timer"); + final Timer buttonActionTimer = new Timer("Mi Band Button Action Timer"); + buttonActionTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + runButtonAction(); + buttonActionTimer.cancel(); + } + }, buttonActionDelay, buttonActionDelay); + } + else { + LOG.info("Activating button action"); + runButtonAction(); + } + currentButtonActionId++; + currentButtonPressCount = 0; + } + } + } + @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { @@ -811,54 +887,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); } + return false; } - public void handleButtonPressed(byte[] value) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.getContext()); - - LOG.info("Button pressed"); - logMessageContent(value); - - Prefs prefs = GBApplication.getPrefs(); - - // If disabled we return from function immediately - if (!prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_ENABLE, false)) { - return; - } - - int buttonPressMaxDelay = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_MAX_DELAY, 2000); - int requiredButtonPressCount = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_COUNT, 0); - - String requiredButtonPressMessage = prefs.getString(MiBandConst.PREF_MIBAND_BUTTON_PRESS_BROADCAST, - this.getContext().getString(R.string.mi2_prefs_button_press_broadcast_default_value)); - - if (requiredButtonPressCount > 0) { - long timeSinceLastPress = System.currentTimeMillis() - currentButtonPressTime; - - if ((currentButtonPressTime == 0) || (timeSinceLastPress < buttonPressMaxDelay)) { - currentButtonPressCount++; - } - else { - currentButtonPressCount = 0; - } - - currentButtonPressTime = System.currentTimeMillis(); - if (currentButtonPressCount >= requiredButtonPressCount) { - Intent in = new Intent(); - in.setAction(requiredButtonPressMessage); - this.getContext().getApplicationContext().sendBroadcast(in); - - currentButtonPressCount = 0; - currentButtonPressTime = System.currentTimeMillis(); - - if (prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_VIBRATE, false)) { - performPreferredNotification(null, null, null, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, null); - } - } - } - } - private void handleUnknownCharacteristic(byte[] value) { } @@ -888,6 +920,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { LOG.info("Unhandled characteristic read: " + characteristicUUID); logMessageContent(characteristic.getValue()); } + return false; } @@ -1063,6 +1096,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { // getContext().getString(R.string.DEVINFO_HR_VER), // info.getSoftwareRevision())); // } + LOG.warn("Device info: " + info); versionCmd.hwVersion = info.getHardwareRevision(); // versionCmd.fwVersion = info.getFirmwareRevision(); // always null diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/AbstractFetchOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/AbstractFetchOperation.java new file mode 100644 index 000000000..2c95d72f3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/AbstractFetchOperation.java @@ -0,0 +1,193 @@ +/* Copyright (C) 2017 Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.SharedPreferences; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.text.DateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +/** + * An operation that fetches activity data. For every fetch, a new operation must + * be created, i.e. an operation may not be reused for multiple fetches. + */ +public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { + private static final Logger LOG = LoggerFactory.getLogger(AbstractFetchOperation.class); + + protected byte lastPacketCounter; + protected int fetchCount; + protected BluetoothGattCharacteristic characteristicActivityData; + protected BluetoothGattCharacteristic characteristicFetch; + protected Calendar startTimestamp; + + public AbstractFetchOperation(MiBand2Support support) { + super(support); + } + + @Override + protected void enableNeededNotifications(TransactionBuilder builder, boolean enable) { + if (!enable) { + // dynamically enabled, but always disabled on finish + builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA), enable); + } + } + + @Override + protected void doPerform() throws IOException { + startFetching(); + } + + protected void startFetching() throws IOException { + lastPacketCounter = -1; + + TransactionBuilder builder = performInitialized("fetching activity data"); + getSupport().setLowLatency(builder); + if (fetchCount == 0) { + builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext())); + } + fetchCount++; + + characteristicActivityData = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA); + builder.notify(characteristicActivityData, false); + + characteristicFetch = getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4); + builder.notify(characteristicFetch, true); + + startFetching(builder); + builder.queue(getQueue()); + } + + protected abstract void startFetching(TransactionBuilder builder); + + protected abstract String getLastSyncTimeKey(); + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + UUID characteristicUUID = characteristic.getUuid(); + if (MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA.equals(characteristicUUID)) { + handleActivityNotif(characteristic.getValue()); + return true; + } else if (MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) { + handleActivityMetadata(characteristic.getValue()); + return true; + } else { + return super.onCharacteristicChanged(gatt, characteristic); + } + } + + @CallSuper + protected void handleActivityFetchFinish() { + operationFinished(); + unsetBusy(); + } + + /** + * Method to handle the incoming activity data. + * There are two kind of messages we currently know: + * - the first one is 11 bytes long and contains metadata (how many bytes to expect, when the data starts, etc.) + * - the second one is 20 bytes long and contains the actual activity data + *

+ * The first message type is parsed by this method, for every other length of the value param, bufferActivityData is called. + * + * @param value + */ + protected abstract void handleActivityNotif(byte[] value); + + /** + * Creates samples from the given 17-length array + * @param value + */ + protected abstract void bufferActivityData(byte[] value); + + protected void handleActivityMetadata(byte[] value) { + if (value.length == 15) { + // first two bytes are whether our request was accepted + if (ArrayUtils.equals(value, MiBand2Service.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) { + // the third byte (0x01 on success) = ? + // the 4th - 7th bytes probably somehow represent the number of bytes/packets to expect + + // last 8 bytes are the start date + Calendar startTimestamp = getSupport().fromTimeBytes(org.apache.commons.lang3.ArrayUtils.subarray(value, 7, value.length)); + setStartTimestamp(startTimestamp); + + GB.toast(getContext().getString(R.string.FetchActivityOperation_about_to_transfer_since, + DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); + } else { + LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + handleActivityFetchFinish(); + } + } else if (value.length == 3) { + if (Arrays.equals(MiBand2Service.RESPONSE_FINISH_SUCCESS, value)) { + handleActivityFetchFinish(); + } else { + LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + handleActivityFetchFinish(); + } + } else { + LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); + handleActivityFetchFinish(); + } + } + + protected void setStartTimestamp(Calendar startTimestamp) { + this.startTimestamp = startTimestamp; + } + + protected void saveLastSyncTimestamp(@NonNull GregorianCalendar timestamp) { + SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit(); + editor.putLong(getLastSyncTimeKey(), timestamp.getTimeInMillis()); + editor.apply(); + } + + + protected GregorianCalendar getLastSuccessfulSyncTime() { + long timeStampMillis = GBApplication.getPrefs().getLong(getLastSyncTimeKey(), 0); + if (timeStampMillis != 0) { + GregorianCalendar calendar = BLETypeConversions.createCalendar(); + calendar.setTimeInMillis(timeStampMillis); + return calendar; + } + GregorianCalendar calendar = BLETypeConversions.createCalendar(); + calendar.add(Calendar.DAY_OF_MONTH, -10); + return calendar; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java index 927af487f..36999acec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java @@ -16,11 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCharacteristic; -import android.content.SharedPreferences; -import android.support.annotation.NonNull; -import android.support.v4.util.TimeUtils; import android.text.format.DateUtils; import android.widget.Toast; @@ -28,18 +23,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.text.DateFormat; 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 java.util.concurrent.TimeUnit; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.Logging; -import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; @@ -51,11 +41,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; -import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation; -import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -63,95 +50,31 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; * An operation that fetches activity data. For every fetch, a new operation must * be created, i.e. an operation may not be reused for multiple fetches. */ -public class FetchActivityOperation extends AbstractMiBand2Operation { +public class FetchActivityOperation extends AbstractFetchOperation { private static final Logger LOG = LoggerFactory.getLogger(FetchActivityOperation.class); private List samples = new ArrayList<>(60*24); // 1day per default - private byte lastPacketCounter; - private Calendar startTimestamp; - private int fetchCount; - public FetchActivityOperation(MiBand2Support support) { super(support); } @Override - protected void enableNeededNotifications(TransactionBuilder builder, boolean enable) { - if (!enable) { - // dynamically enabled, but always disabled on finish - builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA), enable); - } + protected void startFetching() throws IOException { + samples.clear(); + super.startFetching(); } @Override - protected void doPerform() throws IOException { - startFetching(); - } - - private void startFetching() throws IOException { - samples.clear(); - lastPacketCounter = -1; - - TransactionBuilder builder = performInitialized("fetching activity data"); - getSupport().setLowLatency(builder); - if (fetchCount == 0) { - builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext())); - } - fetchCount++; - - BluetoothGattCharacteristic characteristicActivityData = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA); - builder.notify(characteristicActivityData, false); - - BluetoothGattCharacteristic characteristicFetch = getCharacteristic(MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4); - builder.notify(characteristicFetch, true); - + protected void startFetching(TransactionBuilder builder) { GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); - builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, 0x01 }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); + builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, MiBand2Service.COMMAND_ACTIVITY_DATA_TYPE_ACTIVTY }, getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply builder.notify(characteristicActivityData, true); builder.write(characteristicFetch, new byte[] { MiBand2Service.COMMAND_FETCH_ACTIVITY_DATA }); - builder.queue(getQueue()); } - private GregorianCalendar getLastSuccessfulSyncTime() { - long timeStampMillis = GBApplication.getPrefs().getLong(getLastSyncTimeKey(), 0); - if (timeStampMillis != 0) { - GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.setTimeInMillis(timeStampMillis); - return calendar; - } - GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.add(Calendar.DAY_OF_MONTH, -10); - return calendar; - } - - private void saveLastSyncTimestamp(@NonNull GregorianCalendar timestamp) { - SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit(); - editor.putLong(getLastSyncTimeKey(), timestamp.getTimeInMillis()); - editor.apply(); - } - - private String getLastSyncTimeKey() { - return getDevice().getAddress() + "_" + "lastSyncTimeMillis"; - } - - @Override - public boolean onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { - UUID characteristicUUID = characteristic.getUuid(); - if (MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA.equals(characteristicUUID)) { - handleActivityNotif(characteristic.getValue()); - return true; - } else if (MiBand2Service.UUID_UNKNOWN_CHARACTERISTIC4.equals(characteristicUUID)) { - handleActivityMetadata(characteristic.getValue()); - return true; - } else { - return super.onCharacteristicChanged(gatt, characteristic); - } - } - - private void handleActivityFetchFinish() { + protected void handleActivityFetchFinish() { LOG.info("Fetching activity data has finished round " + fetchCount); GregorianCalendar lastSyncTimestamp = saveSamples(); if (lastSyncTimestamp != null && needsAnotherFetch(lastSyncTimestamp)) { @@ -163,8 +86,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { } } - operationFinished(); - unsetBusy(); + super.handleActivityFetchFinish(); } private boolean needsAnotherFetch(GregorianCalendar lastSyncTimestamp) { @@ -232,7 +154,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { * * @param value */ - private void handleActivityNotif(byte[] value) { + protected void handleActivityNotif(byte[] value) { if (!isOperationRunning()) { LOG.error("ignoring activity data notification because operation is not running. Data length: " + value.length); getSupport().logMessageContent(value); @@ -257,7 +179,7 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { * Creates samples from the given 17-length array * @param value */ - private void bufferActivityData(byte[] value) { + protected void bufferActivityData(byte[] value) { int len = value.length; if (len % 4 != 1) { @@ -280,34 +202,8 @@ public class FetchActivityOperation extends AbstractMiBand2Operation { return sample; } - private void handleActivityMetadata(byte[] value) { - if (value.length == 15) { - // first two bytes are whether our request was accepted - if (ArrayUtils.equals(value, MiBand2Service.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) { - // the third byte (0x01 on success) = ? - // the 4th - 7th bytes probably somehow represent the number of bytes/packets to expect - - // last 8 bytes are the start date - Calendar startTimestamp = getSupport().fromTimeBytes(org.apache.commons.lang3.ArrayUtils.subarray(value, 7, value.length)); - setStartTimestamp(startTimestamp); - - GB.toast(getContext().getString(R.string.FetchActivityOperation_about_to_transfer_since, - DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); - } else { - LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); - handleActivityFetchFinish(); - } - } else if (value.length == 3) { - if (Arrays.equals(MiBand2Service.RESPONSE_FINISH_SUCCESS, value)) { - handleActivityFetchFinish(); - } else { - LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); - handleActivityFetchFinish(); - } - } - } - - private void setStartTimestamp(Calendar startTimestamp) { - this.startTimestamp = startTimestamp; + @Override + protected String getLastSyncTimeKey() { + return getDevice().getAddress() + "_" + "lastSyncTimeMillis"; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index 85c99041c..54a40c4e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017 protomors + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1; import android.bluetooth.BluetoothGatt; @@ -114,6 +130,15 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { case No1F1Constants.CMD_FETCH_STEPS: handleStepData(data); return true; + case No1F1Constants.CMD_FETCH_SLEEP: + handleSleepData(data); + return true; + case No1F1Constants.CMD_FETCH_HEARTRATE: + handleHeartRateData(data); + return true; + case No1F1Constants.CMD_REALTIME_HEARTRATE: + handleRealtimeHeartRateData(data); + return true; case No1F1Constants.CMD_NOTIFICATION: case No1F1Constants.CMD_ICON: case No1F1Constants.CMD_DEVICE_SETTINGS: @@ -237,7 +262,17 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { @Override public void onHeartRateTest() { - + try { + TransactionBuilder builder = performInitialized("heartRateTest"); + byte[] msg = new byte[]{ + No1F1Constants.CMD_REALTIME_HEARTRATE, + (byte) 0x11 + }; + builder.write(ctrlCharacteristic, msg); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error starting heart rate measurement: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } } @Override @@ -470,10 +505,18 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { } samples.clear(); LOG.info("Steps data saved"); - if (getDevice().isBusy()) { - getDevice().unsetBusyTask(); - getDevice().sendDeviceUpdateIntent(getContext()); + try { + TransactionBuilder builder = performInitialized("fetchSleep"); + byte[] msg = new byte[]{ + No1F1Constants.CMD_FETCH_SLEEP, + (byte) 0xfa + }; + builder.write(ctrlCharacteristic, msg); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); } + } catch (Exception ex) { GB.toast(getContext(), "Error saving step data: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); } @@ -498,4 +541,132 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { ); } } + + private void handleSleepData(byte[] data) { + if (data[1] == (byte) 0xfd) { + // TODO Check CRC + if (samples.size() > 0) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + No1F1SampleProvider provider = new No1F1SampleProvider(getDevice(), dbHandler.getDaoSession()); + for (int i = 0; i < samples.size(); i++) { + samples.get(i).setDeviceId(deviceId); + samples.get(i).setUserId(userId); + if (samples.get(i).getRawIntensity()<7) + samples.get(i).setRawKind(ActivityKind.TYPE_DEEP_SLEEP); + else + samples.get(i).setRawKind(ActivityKind.TYPE_LIGHT_SLEEP); + provider.addGBActivitySample(samples.get(i)); + } + samples.clear(); + LOG.info("Sleep data saved"); + try { + TransactionBuilder builder = performInitialized("fetchHeartRate"); + byte[] msg = new byte[]{ + No1F1Constants.CMD_FETCH_HEARTRATE, + (byte) 0xfa + }; + builder.write(ctrlCharacteristic, msg); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error fetching heart rate data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + + } catch (Exception ex) { + GB.toast(getContext(), "Error saving sleep data: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + } else { + No1F1ActivitySample sample = new No1F1ActivitySample(); + + Calendar timestamp = GregorianCalendar.getInstance(); + timestamp.set(Calendar.YEAR, data[1] * 256 + (data[2] & 0xff)); + timestamp.set(Calendar.MONTH, (data[3] - 1) & 0xff); + timestamp.set(Calendar.DAY_OF_MONTH, data[4] & 0xff); + timestamp.set(Calendar.HOUR_OF_DAY, data[5] & 0xff); + timestamp.set(Calendar.MINUTE, data[6] & 0xff); + timestamp.set(Calendar.SECOND, 0); + + sample.setTimestamp((int) (timestamp.getTimeInMillis() / 1000L)); + sample.setRawIntensity(data[7] * 256 + (data[8] & 0xff)); + + samples.add(sample); + LOG.info("Received sleep data for " + String.format("%1$TD %1$TT", timestamp) + ": " + + sample.getRawIntensity() + " rolls" + ); + } + } + + private void handleHeartRateData(byte[] data) { + if (data[1] == (byte) 0xfd) { + // TODO Check CRC + if (samples.size() > 0) { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + No1F1SampleProvider provider = new No1F1SampleProvider(getDevice(), dbHandler.getDaoSession()); + for (int i = 0; i < samples.size(); i++) { + samples.get(i).setDeviceId(deviceId); + samples.get(i).setUserId(userId); + provider.addGBActivitySample(samples.get(i)); + } + samples.clear(); + LOG.info("Heart rate data saved"); + if (getDevice().isBusy()) { + getDevice().unsetBusyTask(); + getDevice().sendDeviceUpdateIntent(getContext()); + } + } catch (Exception ex) { + GB.toast(getContext(), "Error saving heart rate data: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + } else { + No1F1ActivitySample sample = new No1F1ActivitySample(); + + Calendar timestamp = GregorianCalendar.getInstance(); + timestamp.set(Calendar.YEAR, data[1] * 256 + (data[2] & 0xff)); + timestamp.set(Calendar.MONTH, (data[3] - 1) & 0xff); + timestamp.set(Calendar.DAY_OF_MONTH, data[4] & 0xff); + timestamp.set(Calendar.HOUR_OF_DAY, data[5] & 0xff); + timestamp.set(Calendar.MINUTE, data[6] & 0xff); + timestamp.set(Calendar.SECOND, 0); + + sample.setTimestamp((int) (timestamp.getTimeInMillis() / 1000L)); + sample.setHeartRate(data[7] & 0xff); + + samples.add(sample); + LOG.info("Received heart rate data for " + String.format("%1$TD %1$TT", timestamp) + ": " + + sample.getHeartRate() + " BPM" + ); + } + } + + private void handleRealtimeHeartRateData(byte[] data) { + if (data.length==2) + { + if (data[1]==(byte) 0x11) + LOG.info("Heart rate measurement started."); + else + LOG.info("Heart rate measurement stopped."); + return; + } + // Check if data is valid. Otherwise ignore sample. + if (data[2]==0) { + No1F1ActivitySample sample = new No1F1ActivitySample(); + sample.setTimestamp((int) (GregorianCalendar.getInstance().getTimeInMillis() / 1000L)); + sample.setHeartRate(data[3] & 0xff); + LOG.info("Current heart rate is: " + sample.getHeartRate() + " BPM"); + try (DBHandler dbHandler = GBApplication.acquireDB()) { + Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId(); + Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId(); + No1F1SampleProvider provider = new No1F1SampleProvider(getDevice(), dbHandler.getDaoSession()); + sample.setDeviceId(deviceId); + sample.setUserId(userId); + provider.addGBActivitySample(sample); + } catch (Exception ex) { + LOG.warn("Error saving current heart rate: " + ex.getLocalizedMessage()); + } + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index ff0eb40d5..cc9cc995c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -36,6 +36,8 @@ import java.util.Random; import java.util.SimpleTimeZone; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement; @@ -591,9 +593,9 @@ public class PebbleProtocol extends GBDeviceProtocol { byte actions_count; short actions_length; String dismiss_string; - String open_string = "Open on phone"; - String mute_string = "Mute"; - String reply_string = "Reply"; + String open_string = GBApplication.getContext().getString(R.string._pebble_watch_open_on_phone); + String mute_string = GBApplication.getContext().getString(R.string._pebble_watch_mute); + String reply_string = GBApplication.getContext().getString(R.string._pebble_watch_reply); if (sourceName != null) { mute_string += " " + sourceName; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java index 379475ca6..e008a590e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti + Gobbetti, Gabe Schrecker This file is part of Gadgetbridge. @@ -18,17 +18,23 @@ package nodomain.freeyourgadget.gadgetbridge.service.receivers; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.media.AudioManager; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; import android.os.SystemClock; import android.view.KeyEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; +import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class GBMusicControlReceiver extends BroadcastReceiver { @@ -73,6 +79,22 @@ public class GBMusicControlReceiver extends BroadcastReceiver { Prefs prefs = GBApplication.getPrefs(); String audioPlayer = prefs.getString("audio_player", "default"); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + MediaSessionManager mediaSessionManager = + (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); + + List controllers = mediaSessionManager.getActiveSessions( + new ComponentName(context, NotificationListener.class)); + try { + MediaController controller = controllers.get(0); + audioPlayer = controller.getPackageName(); + } catch (IndexOutOfBoundsException e) { + System.err.println("IndexOutOfBoundsException: " + e.getMessage()); + } + } + + LOG.debug("keypress: " + musicCmd.toString() + " sent to: " + audioPlayer); + long eventtime = SystemClock.uptimeMillis(); Intent downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index 06bafd277..5b9f83671 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -70,14 +70,6 @@ public class AndroidUtils { } } - public static void setLanguage(Activity activity, Locale language, boolean recreate) { - setLanguage(activity.getBaseContext(), language); - - if (recreate) { - activity.recreate(); - } - } - public static void setLanguage(Context context, Locale language) { Configuration config = new Configuration(); config.setLocale(language); 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 ab5f8c812..4f1201cc3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, - Daniele Gobbetti, João Paulo Barraca + Daniele Gobbetti, João Paulo Barraca, protomors, Quallenauge, Sami Alaoui This file is part of Gadgetbridge. @@ -40,6 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip.AmazfitBipCooordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30Coordinator; @@ -196,6 +197,7 @@ public class DeviceHelper { result.add(new HPlusCoordinator()); result.add(new No1F1Coordinator()); result.add(new MakibesF68Coordinator()); + result.add(new EXRIZUK8Coordinator()); result.add(new TeclastH30Coordinator()); return result; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java index 82a397f78..855241cf2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Carsten Pfeiffer +/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Michal Novotny This file is part of Gadgetbridge. @@ -68,4 +68,4 @@ public class Version implements Comparable { public int hashCode() { return version.hashCode(); } -} \ No newline at end of file +} diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 4d92f1c78..793f73286 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -413,13 +413,14 @@ Definujte počet stisknutí tlačítka k vyslání broadcast zprávy Zpráva k vyslání (broadcast) Zpráva k vyslání do systému pokud počtu stisknutí tlačítka (viz výše) - nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed Povolit akci tlačítka Povolit akci tlačítka na definovaný počet stisknutí Povolit vibrace náramku Povolit vibrace náramku při vyslání broadcast zprávy Maximální prodleva mezi stisky Maximální prodleva mezi stisky tlačítka v milisekundách + Prodleva před akcí + Prodleva před akcí tlačítka. Umožňuje více průchodů (v extra button_id u Intent) nebo 0 pro okamžitou akci Chystáte se nainstalovat firmware %s namísto toho, který je aktuálně na vašem Amazfit Bipu. \n \nUjistěte se, že jste nainstalovali firmware .gps, pak soubor .res a nakonec soubor .fw. Hodiny se po instalaci souboru .fw restartují. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e4eafd3b6..d1b775493 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -289,7 +289,7 @@ Aktivitätsdaten verbleiben auf dem Mi Band, auch nach der Synchronisierung. Hilfreich, wenn das Mi Band mit weiteren Apps verwendet wird. Benutze Modus mit niedriger Latenz für Firmware-Updates Dies kann bei Geräten helfen, bei denen Firmwareupdates fehlschlagen - Verlauf Schritte + Schritteverlauf Akt. Schritte pro Minute Schritte insgesamt Verlauf Schritte pro Minute @@ -385,7 +385,7 @@ Wecker (%1$s) Gefunden! - Mi2: Uhrzeit-Format + Mi2: Uhrzeitformat Installiere Version %1$s vor dem Installieren der Firmware! Text Benachrichtigung = 1.0.1.28 und installiertes Mili_pro.ft* benötigt.]]> @@ -410,4 +410,19 @@ \nHinweis: Du musst die .res and .gps Dateien nicht installieren, falls diese genau die gleichen Dateien wie die sind, die Du schon mit einer vorigen .fw Datei zusammen installiert hattest. \n \nDIES IST EXPERIMENTELL, FAHRE AUF EIGENES RISIKO FORT - + Amazfit Bip Firmware %1$s + Aktion bei Tastendruck + Bestimmte Aktion bei Tastendruck auf dem Mi Band 2 + Anzahl der Tastendrücke, die einen Broadcast auslöst + Zu Sendende Broadcast-Nachricht + Aktiviere Tastenaktion + Aktiviere Aktion bei einer bestimmten Anzahl an Tastendrücken + Aktiviere Vibration + Vibriere, wenn die Tastenaktion ausgelöst wurde + Maximale Verzögerung zwischen den Tastendrücken + Maximale Verzögerung zwischen den Tastendrücken in Millisekunden + Verzögerung nach Tastenaktion + Auf dem Smartphone öffnen + Stummschalten + Antwort + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ae1a4b1ee..68fd3c432 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -411,4 +411,22 @@ \n \nEXPERIMENTAL, PROCEDE BAJO TU PROPIA RESPONSABILIDAD Firmware Amazfit Bip %1$s - + Acciones del botón + Especificar acción para pulsación del botón del Mi Band 2 + Número de presiones + Número de presiones para activar el envío del mensaje + Mensaje para enviar + Enviar mensaje después de un número definido de pulsaciones + nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed + Activar acción por botón + Activar acción por un número definido de pulsaciones + Activar la vibración de la pulsera + Activar vibración por acción + Retardo máximo entre pulsaciones + Retardo máximo entre pulsaciones en milisegundos + Retardo después de la acción del botón + Retardo después de una acción del botón (el número está en button_id intent extra) o bien 0 para efecto inmediato + Abrir en el teléfono + Silenciar + Responder + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 19e87a46e..cf284f61d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -415,4 +415,22 @@ NOTE: la base de données sera bien évidement plus grande ! \n \nEXPÉRIMENTAL, CONTINUEZ À VOS RISQUES Firmware Amazfit Bip %1$s - + Actions du bouton + Spécifier les actions par pression du bouton du Mi Band 2 + Nombre de pressions du bouton + Nombre de pressions pour envoyer message + Message à envoyer + Envoyer message après nombre défini de pressions du bouton + nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed + Activer action du bouton + Activer action après nombre spécifié de pressions + Activer la vibration du bracelet + Activer la vibration après déclenchement de l\'action + Délai maximum entre pressions + Délai maximum entre pressions en millisecondes + Délai après action du bouton + Délai après une pression de bouton (le nombre est dans button_id intent extra) ou 0 pour immédiatement + Ouvrir sur le téléphone + Silencieux + Répondre + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 281652d35..2b1cf3d2b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -409,4 +409,17 @@ \n \n実験的です。あなた自身の責任で行ってください Amazfit Bip ファームウェア%1$s + ボタンの動作 + Mi Band 2 ボタンを押した動作を指定します + ボタン押したカウント + ブロードキャスト メッセージをトリガーするボタンを押した数 + 送信するブロードキャスト メッセージ + ボタンを押した数で定義された数に達したブロードキャスト メッセージ + nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed + ボタンの動作を有効にする + 指定したボタンを押した数の動作を有効にします + band の振動を有効にする + ボタンの動作をトリガーに band の振動を有効にします + ボタンを押す間の最大遅延時間 + ボタンを押す間隔の最大遅延時間 (ミリ秒単位) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 5256737b7..7e92403a0 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -7,7 +7,7 @@ 종료 동기화 수면 측정계 (초기 버전) - 잃어버린 기기 찾기... + 잃어버린 기기 찾기 스크린샷 찍기 연결 해제 디버그 @@ -46,7 +46,7 @@ SMS Pebble 메세지 일반적인 알림 지원 - … 화면이 켜져있을 때도 + …화면이 켜져있을 때도 항상 화면이 꺼져 있을 때 하지 않음 @@ -56,7 +56,7 @@ Mi Band 주소 Pebble 설정 선호하는 액티비티 트래커 - 제3자 안드로이드 앱 접근을 허용 + 제 3자 안드로이드 앱 접근을 허용 PebbleKit을 사용하는 안드로이드 앱을 실험적으로 지원 강제 알림 프로토콜 이 옵션은 펌웨어 버전에 따라 최신 알림 프로토콜을 강제로 사용하도록 합니다. 자세한 내용을 알 경우에만 이 옵션을 사용하세요. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68d808fbb..c12fce176 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,7 +92,7 @@ Generic notification support …also when screen is on Do Not Disturb - Stop unwanted Notifications from being sent based on the Do Not Disturb mode + Stop unwanted notifications from being sent based on the Do Not Disturb mode Transliteration Enable this if your device has no support for your language\'s font @@ -158,7 +158,7 @@ Please enable network location location acquired - Force Notification Protocol + Force notification protocol This option forces using the latest notification protocol depending on the firmware version. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING! Enable untested features Enable features that are untested. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING! @@ -357,13 +357,15 @@ Number of button presses to trigger message broadcast Broadcast message to send Broadcast message on defined number of button presses reached - nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed + nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed Enable button action Enable action on specified number of button presses Enable band vibration Enable band vibration on button action triggered Maximum delay between presses Maximum delay between button presses in milliseconds + Delay after button action + Delay after one button action match (number is in button_id intent extra) or 0 for immediately Goal notification The band will vibrate when the daily steps goal is reached Display items @@ -471,4 +473,9 @@ Select Pair to pair your devices. If this fails, try again without pairing. Pair Don\'t Pair + + + Open on phone + Mute + Reply diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index a6d9f3501..4cf3ec831 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,14 @@ + + Initial support for EXRIZU K8 (HPLus variant) + Amazfit Bip: fix long messages not being displayed at all + Mi Band 2: Support multiple button actions + NO.1 F1: Fetch sleep data + NO.1 F1: Heart rate support + Pebble: Support controlling the current active media playback application + Fix suspended activities coming to front when rotating the screen + Initial NO.1 F1 support Initial Teclast H30 support diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 0dcc54924..3798c8f66 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -49,12 +49,14 @@ + +