mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-26 10:35:50 +01:00
Merge branch 'master' into background-javascript
This commit is contained in:
commit
99d873a178
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Daniele Gobbetti
|
||||
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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) {
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.jyou;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.jyou;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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;
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.no1f1;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -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,");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.jyou;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
|
@ -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<MiBand2Support> 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
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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
|
||||
* <p/>
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -16,11 +16,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
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<MiBandActivitySample> 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";
|
||||
}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<MediaController> 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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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<Version> {
|
||||
public int hashCode() {
|
||||
return version.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -413,13 +413,14 @@
|
||||
<string name="mi2_prefs_button_press_count_summary">Definujte počet stisknutí tlačítka k vyslání broadcast zprávy</string>
|
||||
<string name="mi2_prefs_button_press_broadcast">Zpráva k vyslání (broadcast)</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_summary">Zpráva k vyslání do systému pokud počtu stisknutí tlačítka (viz výše)</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_default_value">nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed</string>
|
||||
<string name="mi2_prefs_button_action">Povolit akci tlačítka</string>
|
||||
<string name="mi2_prefs_button_action_summary">Povolit akci tlačítka na definovaný počet stisknutí</string>
|
||||
<string name="mi2_prefs_button_action_vibrate">Povolit vibrace náramku</string>
|
||||
<string name="mi2_prefs_button_action_vibrate_summary">Povolit vibrace náramku při vyslání broadcast zprávy</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay">Maximální prodleva mezi stisky</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay_summary">Maximální prodleva mezi stisky tlačítka v milisekundách</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay">Prodleva před akcí</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay_summary">Prodleva před akcí tlačítka. Umožňuje více průchodů (v extra button_id u Intent) nebo 0 pro okamžitou akci</string>
|
||||
<string name="fw_upgrade_notice_amazfitbip">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í.
|
||||
|
@ -289,7 +289,7 @@
|
||||
<string name="pref_summary_keep_data_on_device">Aktivitätsdaten verbleiben auf dem Mi Band, auch nach der Synchronisierung. Hilfreich, wenn das Mi Band mit weiteren Apps verwendet wird.</string>
|
||||
<string name="pref_title_low_latency_fw_update">Benutze Modus mit niedriger Latenz für Firmware-Updates</string>
|
||||
<string name="pref_summary_low_latency_fw_update">Dies kann bei Geräten helfen, bei denen Firmwareupdates fehlschlagen</string>
|
||||
<string name="live_activity_steps_history">Verlauf Schritte</string>
|
||||
<string name="live_activity_steps_history">Schritteverlauf</string>
|
||||
<string name="live_activity_current_steps_per_minute">Akt. Schritte pro Minute</string>
|
||||
<string name="live_activity_total_steps">Schritte insgesamt</string>
|
||||
<string name="live_activity_steps_per_minute_history">Verlauf Schritte pro Minute</string>
|
||||
@ -385,7 +385,7 @@
|
||||
<string name="pref_screen_notification_profile_alarm_clock">Wecker</string>
|
||||
<string name="StringUtils_sender"> (%1$s)</string>
|
||||
<string name="find_device_you_found_it">Gefunden!</string>
|
||||
<string name="miband2_prefs_timeformat">Mi2: Uhrzeit-Format</string>
|
||||
<string name="miband2_prefs_timeformat">Mi2: Uhrzeitformat</string>
|
||||
<string name="mi2_fw_installhandler_fw53_hint">Installiere Version %1$s vor dem Installieren der Firmware!</string>
|
||||
<string name="mi2_enable_text_notifications">Text Benachrichtigung</string>
|
||||
<string name="mi2_enable_text_notifications_summary"><![CDATA[Firmware >= 1.0.1.28 und installiertes Mili_pro.ft* benötigt.]]></string>
|
||||
@ -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</string>
|
||||
</resources>
|
||||
<string name="amazfitbip_firmware">Amazfit Bip Firmware %1$s</string>
|
||||
<string name="mi2_prefs_button_actions">Aktion bei Tastendruck</string>
|
||||
<string name="mi2_prefs_button_actions_summary">Bestimmte Aktion bei Tastendruck auf dem Mi Band 2</string>
|
||||
<string name="mi2_prefs_button_press_count_summary">Anzahl der Tastendrücke, die einen Broadcast auslöst</string>
|
||||
<string name="mi2_prefs_button_press_broadcast">Zu Sendende Broadcast-Nachricht</string>
|
||||
<string name="mi2_prefs_button_action">Aktiviere Tastenaktion</string>
|
||||
<string name="mi2_prefs_button_action_summary">Aktiviere Aktion bei einer bestimmten Anzahl an Tastendrücken</string>
|
||||
<string name="mi2_prefs_button_action_vibrate">Aktiviere Vibration</string>
|
||||
<string name="mi2_prefs_button_action_vibrate_summary">Vibriere, wenn die Tastenaktion ausgelöst wurde</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay">Maximale Verzögerung zwischen den Tastendrücken</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay_summary">Maximale Verzögerung zwischen den Tastendrücken in Millisekunden</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay">Verzögerung nach Tastenaktion</string>
|
||||
<string name="_pebble_watch_open_on_phone">Auf dem Smartphone öffnen</string>
|
||||
<string name="_pebble_watch_mute">Stummschalten</string>
|
||||
<string name="_pebble_watch_reply">Antwort</string>
|
||||
</resources>
|
||||
|
@ -411,4 +411,22 @@
|
||||
\n
|
||||
\nEXPERIMENTAL, PROCEDE BAJO TU PROPIA RESPONSABILIDAD</string>
|
||||
<string name="amazfitbip_firmware">Firmware Amazfit Bip %1$s</string>
|
||||
</resources>
|
||||
<string name="mi2_prefs_button_actions">Acciones del botón</string>
|
||||
<string name="mi2_prefs_button_actions_summary">Especificar acción para pulsación del botón del Mi Band 2</string>
|
||||
<string name="mi2_prefs_button_press_count">Número de presiones</string>
|
||||
<string name="mi2_prefs_button_press_count_summary">Número de presiones para activar el envío del mensaje</string>
|
||||
<string name="mi2_prefs_button_press_broadcast">Mensaje para enviar</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_summary">Enviar mensaje después de un número definido de pulsaciones</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_default_value">nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed</string>
|
||||
<string name="mi2_prefs_button_action">Activar acción por botón</string>
|
||||
<string name="mi2_prefs_button_action_summary">Activar acción por un número definido de pulsaciones</string>
|
||||
<string name="mi2_prefs_button_action_vibrate">Activar la vibración de la pulsera</string>
|
||||
<string name="mi2_prefs_button_action_vibrate_summary">Activar vibración por acción</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay">Retardo máximo entre pulsaciones</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay_summary">Retardo máximo entre pulsaciones en milisegundos</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay">Retardo después de la acción del botón</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay_summary">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</string>
|
||||
<string name="_pebble_watch_open_on_phone">Abrir en el teléfono</string>
|
||||
<string name="_pebble_watch_mute">Silenciar</string>
|
||||
<string name="_pebble_watch_reply">Responder</string>
|
||||
</resources>
|
||||
|
@ -415,4 +415,22 @@ NOTE: la base de données sera bien évidement plus grande !</string>
|
||||
\n
|
||||
\nEXPÉRIMENTAL, CONTINUEZ À VOS RISQUES</string>
|
||||
<string name="amazfitbip_firmware">Firmware Amazfit Bip %1$s</string>
|
||||
</resources>
|
||||
<string name="mi2_prefs_button_actions">Actions du bouton</string>
|
||||
<string name="mi2_prefs_button_actions_summary">Spécifier les actions par pression du bouton du Mi Band 2</string>
|
||||
<string name="mi2_prefs_button_press_count">Nombre de pressions du bouton</string>
|
||||
<string name="mi2_prefs_button_press_count_summary">Nombre de pressions pour envoyer message</string>
|
||||
<string name="mi2_prefs_button_press_broadcast">Message à envoyer</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_summary">Envoyer message après nombre défini de pressions du bouton</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_default_value">nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed</string>
|
||||
<string name="mi2_prefs_button_action">Activer action du bouton</string>
|
||||
<string name="mi2_prefs_button_action_summary">Activer action après nombre spécifié de pressions</string>
|
||||
<string name="mi2_prefs_button_action_vibrate">Activer la vibration du bracelet</string>
|
||||
<string name="mi2_prefs_button_action_vibrate_summary">Activer la vibration après déclenchement de l\'action</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay">Délai maximum entre pressions</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay_summary">Délai maximum entre pressions en millisecondes</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay">Délai après action du bouton</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay_summary">Délai après une pression de bouton (le nombre est dans button_id intent extra) ou 0 pour immédiatement</string>
|
||||
<string name="_pebble_watch_open_on_phone">Ouvrir sur le téléphone</string>
|
||||
<string name="_pebble_watch_mute">Silencieux</string>
|
||||
<string name="_pebble_watch_reply">Répondre</string>
|
||||
</resources>
|
||||
|
@ -409,4 +409,17 @@
|
||||
\n
|
||||
\n実験的です。あなた自身の責任で行ってください</string>
|
||||
<string name="amazfitbip_firmware">Amazfit Bip ファームウェア%1$s</string>
|
||||
<string name="mi2_prefs_button_actions">ボタンの動作</string>
|
||||
<string name="mi2_prefs_button_actions_summary">Mi Band 2 ボタンを押した動作を指定します</string>
|
||||
<string name="mi2_prefs_button_press_count">ボタン押したカウント</string>
|
||||
<string name="mi2_prefs_button_press_count_summary">ブロードキャスト メッセージをトリガーするボタンを押した数</string>
|
||||
<string name="mi2_prefs_button_press_broadcast">送信するブロードキャスト メッセージ</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_summary">ボタンを押した数で定義された数に達したブロードキャスト メッセージ</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_default_value">nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed</string>
|
||||
<string name="mi2_prefs_button_action">ボタンの動作を有効にする</string>
|
||||
<string name="mi2_prefs_button_action_summary">指定したボタンを押した数の動作を有効にします</string>
|
||||
<string name="mi2_prefs_button_action_vibrate">band の振動を有効にする</string>
|
||||
<string name="mi2_prefs_button_action_vibrate_summary">ボタンの動作をトリガーに band の振動を有効にします</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay">ボタンを押す間の最大遅延時間</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay_summary">ボタンを押す間隔の最大遅延時間 (ミリ秒単位)</string>
|
||||
</resources>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<string name="action_quit">종료</string>
|
||||
<string name="controlcenter_fetch_activity_data">동기화</string>
|
||||
<string name="controlcenter_start_sleepmonitor">수면 측정계 (초기 버전)</string>
|
||||
<string name="controlcenter_find_device">잃어버린 기기 찾기...</string>
|
||||
<string name="controlcenter_find_device">잃어버린 기기 찾기</string>
|
||||
<string name="controlcenter_take_screenshot">스크린샷 찍기</string>
|
||||
<string name="controlcenter_disconnect">연결 해제</string>
|
||||
<string name="title_activity_debug">디버그</string>
|
||||
@ -46,7 +46,7 @@
|
||||
<string name="pref_title_notifications_sms">SMS</string>
|
||||
<string name="pref_title_notifications_pebblemsg">Pebble 메세지</string>
|
||||
<string name="pref_title_notifications_generic">일반적인 알림 지원</string>
|
||||
<string name="pref_title_whenscreenon">… 화면이 켜져있을 때도</string>
|
||||
<string name="pref_title_whenscreenon">…화면이 켜져있을 때도</string>
|
||||
<string name="always">항상</string>
|
||||
<string name="when_screen_off">화면이 꺼져 있을 때</string>
|
||||
<string name="never">하지 않음</string>
|
||||
@ -56,7 +56,7 @@
|
||||
<string name="pref_title_development_miaddr">Mi Band 주소</string>
|
||||
<string name="pref_title_pebble_settings">Pebble 설정</string>
|
||||
<string name="pref_title_pebble_activitytracker">선호하는 액티비티 트래커</string>
|
||||
<string name="pref_title_enable_pebblekit">제3자 안드로이드 앱 접근을 허용</string>
|
||||
<string name="pref_title_enable_pebblekit">제 3자 안드로이드 앱 접근을 허용</string>
|
||||
<string name="pref_summary_enable_pebblekit">PebbleKit을 사용하는 안드로이드 앱을 실험적으로 지원</string>
|
||||
<string name="pref_title_pebble_forceprotocol">강제 알림 프로토콜</string>
|
||||
<string name="pref_summary_pebble_forceprotocol">이 옵션은 펌웨어 버전에 따라 최신 알림 프로토콜을 강제로 사용하도록 합니다. 자세한 내용을 알 경우에만 이 옵션을 사용하세요.</string>
|
||||
|
@ -92,7 +92,7 @@
|
||||
<string name="pref_title_notifications_generic">Generic notification support</string>
|
||||
<string name="pref_title_whenscreenon">…also when screen is on</string>
|
||||
<string name="pref_title_notification_filter">Do Not Disturb</string>
|
||||
<string name="pref_summary_notification_filter">Stop unwanted Notifications from being sent based on the Do Not Disturb mode</string>
|
||||
<string name="pref_summary_notification_filter">Stop unwanted notifications from being sent based on the Do Not Disturb mode</string>
|
||||
<string name="pref_title_transliteration">Transliteration</string>
|
||||
<string name="pref_summary_transliteration">Enable this if your device has no support for your language\'s font</string>
|
||||
|
||||
@ -158,7 +158,7 @@
|
||||
<string name="toast_enable_networklocationprovider">Please enable network location</string>
|
||||
<string name="toast_aqurired_networklocation">location acquired</string>
|
||||
|
||||
<string name="pref_title_pebble_forceprotocol">Force Notification Protocol</string>
|
||||
<string name="pref_title_pebble_forceprotocol">Force notification protocol</string>
|
||||
<string name="pref_summary_pebble_forceprotocol">This option forces using the latest notification protocol depending on the firmware version. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING!</string>
|
||||
<string name="pref_title_pebble_forceuntested">Enable untested features</string>
|
||||
<string name="pref_summary_pebble_forceuntested">Enable features that are untested. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING!</string>
|
||||
@ -357,13 +357,15 @@
|
||||
<string name="mi2_prefs_button_press_count_summary">Number of button presses to trigger message broadcast</string>
|
||||
<string name="mi2_prefs_button_press_broadcast">Broadcast message to send</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_summary">Broadcast message on defined number of button presses reached</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_default_value">nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed</string>
|
||||
<string name="mi2_prefs_button_press_broadcast_default_value" translatable="false">nodomain.freeyourgadget.gadgetbridge.mibandButtonPressed</string>
|
||||
<string name="mi2_prefs_button_action">Enable button action</string>
|
||||
<string name="mi2_prefs_button_action_summary">Enable action on specified number of button presses</string>
|
||||
<string name="mi2_prefs_button_action_vibrate">Enable band vibration</string>
|
||||
<string name="mi2_prefs_button_action_vibrate_summary">Enable band vibration on button action triggered</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay">Maximum delay between presses</string>
|
||||
<string name="mi2_prefs_button_press_count_max_delay_summary">Maximum delay between button presses in milliseconds</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay">Delay after button action</string>
|
||||
<string name="mi2_prefs_button_press_count_match_delay_summary">Delay after one button action match (number is in button_id intent extra) or 0 for immediately</string>
|
||||
<string name="mi2_prefs_goal_notification">Goal notification</string>
|
||||
<string name="mi2_prefs_goal_notification_summary">The band will vibrate when the daily steps goal is reached</string>
|
||||
<string name="mi2_prefs_display_items">Display items</string>
|
||||
@ -471,4 +473,9 @@
|
||||
<string name="discovery_pair_question">Select Pair to pair your devices. If this fails, try again without pairing.</string>
|
||||
<string name="discovery_yes_pair">Pair</string>
|
||||
<string name="discovery_dont_pair">Don\'t Pair</string>
|
||||
|
||||
<!-- strings sent to pebble watches for quick actions -->
|
||||
<string name="_pebble_watch_open_on_phone">Open on phone</string>
|
||||
<string name="_pebble_watch_mute">Mute</string>
|
||||
<string name="_pebble_watch_reply">Reply</string>
|
||||
</resources>
|
||||
|
@ -1,5 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<changelog>
|
||||
<release version="0.21.1" versioncode="102">
|
||||
<change>Initial support for EXRIZU K8 (HPLus variant)</change>
|
||||
<change>Amazfit Bip: fix long messages not being displayed at all</change>
|
||||
<change>Mi Band 2: Support multiple button actions</change>
|
||||
<change>NO.1 F1: Fetch sleep data</change>
|
||||
<change>NO.1 F1: Heart rate support</change>
|
||||
<change>Pebble: Support controlling the current active media playback application</change>
|
||||
<change>Fix suspended activities coming to front when rotating the screen</change>
|
||||
</release>
|
||||
<release version="0.21.0" versioncode="101">
|
||||
<change>Initial NO.1 F1 support</change>
|
||||
<change>Initial Teclast H30 support</change>
|
||||
|
@ -49,12 +49,14 @@
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:key="mi2_button_action_vibrate"
|
||||
android:summary="@string/mi2_prefs_button_action_vibrate_summary"
|
||||
android:title="@string/mi2_prefs_button_action_vibrate" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="6"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:inputType="number"
|
||||
android:key="mi_button_press_count"
|
||||
android:summary="@string/mi2_prefs_button_press_count_summary"
|
||||
@ -62,16 +64,26 @@
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/mi2_prefs_button_press_broadcast_default_value"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:key="mi_button_press_broadcast"
|
||||
android:summary="@string/mi2_prefs_button_press_broadcast_summary"
|
||||
android:title="@string/mi2_prefs_button_press_broadcast" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="2000"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:inputType="number"
|
||||
android:key="mi_button_press_count_max_delay"
|
||||
android:summary="@string/mi2_prefs_button_press_count_max_delay_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count_max_delay" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="0"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:inputType="number"
|
||||
android:key="mi_button_press_count_match_delay"
|
||||
android:summary="@string/mi2_prefs_button_press_count_match_delay_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count_match_delay" />
|
||||
</PreferenceScreen>
|
||||
|
||||
<EditTextPreference
|
||||
|
7
fastlane/metadata/android/en-US/changelogs/102.txt
Normal file
7
fastlane/metadata/android/en-US/changelogs/102.txt
Normal file
@ -0,0 +1,7 @@
|
||||
* 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
|
Loading…
Reference in New Issue
Block a user