1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-24 05:50:47 +02:00

Merge branch 'master' into background-javascript

# Conflicts:
#	app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java
This commit is contained in:
Daniele Gobbetti 2017-09-03 16:49:27 +02:00
commit bb962001a5
41 changed files with 818 additions and 152 deletions

View File

@ -1,5 +1,15 @@
### Changelog ### Changelog
#### Version 0.21.0 (next)
* Initial NO.1 F1 support
* Amazfit Bip: Display GPS firmware version
* Amazfit Bip: Fix E-Mail notifications
* Amazfit Bip: Fix call notification with unknown caller
* Pebble: Fix crash when takeing screenshots on Android 8.0 (Oreo)
* Pebble: Support some google app icons
* Pebble: try to support spotify
* Fix language being reset to system default
#### Version 0.20.2 #### Version 0.20.2
* Amazfit Bip: Various fixes regarding weather, add condition string support for FW 0.0.8.74 * Amazfit Bip: Various fixes regarding weather, add condition string support for FW 0.0.8.74
* Amazfit Bip: enable caller display in later firmwares * Amazfit Bip: enable caller display in later firmwares

View File

@ -6,10 +6,13 @@ Pebble, Mi Band, Amazfit Bit and HPlus device (and more) without the vendor's cl
and without the need to create an account and transmit any of your data to the and without the need to create an account and transmit any of your data to the
vendor's servers. vendor's servers.
[Homepage](https://gadgetbridge.org) [Homepage](https://gadgetbridge.org)
[Blog](https://blog.gadgetbridge.org) [Blog](https://blog.gadgetbridge.org)
[![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Gadgetbridge/donate)
[![Build](https://travis-ci.org/Freeyourgadget/Gadgetbridge.svg?branch=master)](https://travis-ci.org/Freeyourgadget/Gadgetbridge) [![Build](https://travis-ci.org/Freeyourgadget/Gadgetbridge.svg?branch=master)](https://travis-ci.org/Freeyourgadget/Gadgetbridge)
## Download ## Download

View File

@ -26,8 +26,8 @@ android {
targetSdkVersion 25 targetSdkVersion 25
// note: always bump BOTH versionCode and versionName! // note: always bump BOTH versionCode and versionName!
versionName "0.20.2" versionName "0.21.0"
versionCode 100 versionCode 101
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
buildTypes { buildTypes {

View File

@ -59,6 +59,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.service.NotificationCollectorMonitorService; import nodomain.freeyourgadget.gadgetbridge.service.NotificationCollectorMonitorService;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
@ -544,11 +545,11 @@ public class GBApplication extends Application {
} else { } else {
language = new Locale(lang); language = new Locale(lang);
} }
Configuration config = new Configuration(); updateLanguage(language);
config.setLocale(language); }
// FIXME: I have no idea what I am doing public static void updateLanguage(Locale locale) {
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); AndroidUtils.setLanguage(context, locale);
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(ACTION_LANGUAGE_CHANGE); intent.setAction(ACTION_LANGUAGE_CHANGE);
@ -570,6 +571,12 @@ public class GBApplication extends Application {
return typedValue.data; return typedValue.data;
} }
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateLanguage(getLanguage());
}
public static int getBackgroundColor(Context context) { public static int getBackgroundColor(Context context) {
TypedValue typedValue = new TypedValue(); TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme(); Resources.Theme theme = context.getTheme();

View File

@ -0,0 +1,96 @@
/* 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 android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
public abstract class AbstractGBActivity extends AppCompatActivity implements GBActivity {
public static final int NONE = 0;
public static final int NO_ACTIONBAR = 1;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage(), true);
break;
case GBApplication.ACTION_QUIT:
finish();
break;
}
}
};
public void setLanguage(Locale language, boolean recreate) {
AndroidUtils.setLanguage(this, language, recreate);
}
public static void init(GBActivity activity) {
init(activity, NONE);
}
public static void init(GBActivity activity, int flags) {
if (GBApplication.isDarkThemeEnabled()) {
if ((flags & NO_ACTIONBAR) != 0) {
activity.setTheme(R.style.GadgetbridgeThemeDark_NoActionBar);
} else {
activity.setTheme(R.style.GadgetbridgeThemeDark);
}
} else {
if ((flags & NO_ACTIONBAR) != 0) {
activity.setTheme(R.style.GadgetbridgeTheme_NoActionBar);
} else {
activity.setTheme(R.style.GadgetbridgeTheme);
}
}
activity.setLanguage(GBApplication.getLanguage(), false);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBApplication.ACTION_QUIT);
filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
init(this);
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
}

View File

@ -33,7 +33,7 @@ import android.support.v4.app.FragmentPagerAdapter;
* *
* @see AbstractGBFragment * @see AbstractGBFragment
*/ */
public abstract class AbstractGBFragmentActivity extends GBActivity { public abstract class AbstractGBFragmentActivity extends AbstractGBActivity {
/** /**
* The {@link android.support.v4.view.PagerAdapter} that will provide * The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a * fragments for each of the sections. We use a

View File

@ -37,7 +37,6 @@ import org.slf4j.LoggerFactory;
import java.util.Locale; import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
/** /**
@ -46,7 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
* to set that listener in #onCreate, *not* in #onPostCreate, otherwise the value will * to set that listener in #onCreate, *not* in #onPostCreate, otherwise the value will
* not be displayed. * not be displayed.
*/ */
public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivity { public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivity implements GBActivity {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSettingsActivity.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractSettingsActivity.class);
@ -56,7 +55,7 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi
String action = intent.getAction(); String action = intent.getAction();
switch (action) { switch (action) {
case GBApplication.ACTION_LANGUAGE_CHANGE: case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage()); setLanguage(GBApplication.getLanguage(), true);
break; break;
case GBApplication.ACTION_QUIT: case GBApplication.ACTION_QUIT:
finish(); finish();
@ -129,11 +128,7 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
if (GBApplication.isDarkThemeEnabled()) { AbstractGBActivity.init(this);
setTheme(R.style.GadgetbridgeThemeDark);
} else {
setTheme(R.style.GadgetbridgeTheme);
}
IntentFilter filterLocal = new IntentFilter(); IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBApplication.ACTION_QUIT); filterLocal.addAction(GBApplication.ACTION_QUIT);
@ -215,7 +210,7 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private void setLanguage(Locale language) { public void setLanguage(Locale language, boolean recreate) {
AndroidUtils.setLanguage(this, language); AndroidUtils.setLanguage(this, language, recreate);
} }
} }

View File

@ -31,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
public class AlarmDetails extends GBActivity { public class AlarmDetails extends AbstractGBActivity {
private GBAlarm alarm; private GBAlarm alarm;
private TimePicker timePicker; private TimePicker timePicker;

View File

@ -20,7 +20,7 @@ import android.os.Bundle;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
public class AndroidPairingActivity extends GBActivity { public class AndroidPairingActivity extends AbstractGBActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {

View File

@ -31,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.AppBlacklistAdapter; import nodomain.freeyourgadget.gadgetbridge.adapter.AppBlacklistAdapter;
public class AppBlacklistActivity extends GBActivity { public class AppBlacklistActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class); private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class);
private AppBlacklistAdapter appBlacklistAdapter; private AppBlacklistAdapter appBlacklistAdapter;

View File

@ -47,7 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class CalBlacklistActivity extends GBActivity { public class CalBlacklistActivity extends AbstractGBActivity {
private final String[] EVENT_PROJECTION = new String[]{ private final String[] EVENT_PROJECTION = new String[]{
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,

View File

@ -38,7 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS;
public class ConfigureAlarms extends GBActivity { public class ConfigureAlarms extends AbstractGBActivity {
private static final int REQ_CONFIGURE_ALARM = 1; private static final int REQ_CONFIGURE_ALARM = 1;

View File

@ -62,9 +62,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
//TODO: extend GBActivity, but it requires actionbar that is not available //TODO: extend AbstractGBActivity, but it requires actionbar that is not available
public class ControlCenterv2 extends AppCompatActivity public class ControlCenterv2 extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener { implements NavigationView.OnNavigationItemSelectedListener, GBActivity {
//needed for KK compatibility //needed for KK compatibility
static { static {
@ -84,7 +84,7 @@ public class ControlCenterv2 extends AppCompatActivity
String action = intent.getAction(); String action = intent.getAction();
switch (action) { switch (action) {
case GBApplication.ACTION_LANGUAGE_CHANGE: case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage()); setLanguage(GBApplication.getLanguage(), true);
break; break;
case GBApplication.ACTION_QUIT: case GBApplication.ACTION_QUIT:
finish(); finish();
@ -98,11 +98,7 @@ public class ControlCenterv2 extends AppCompatActivity
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
if (GBApplication.isDarkThemeEnabled()) { AbstractGBActivity.init(this, AbstractGBActivity.NO_ACTIONBAR);
setTheme(R.style.GadgetbridgeThemeDark_NoActionBar);
} else {
setTheme(R.style.GadgetbridgeTheme_NoActionBar);
}
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_controlcenterv2); setContentView(R.layout.activity_controlcenterv2);
@ -315,7 +311,7 @@ public class ControlCenterv2 extends AppCompatActivity
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0); ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0);
} }
private void setLanguage(Locale language) { public void setLanguage(Locale language, boolean recreate) {
AndroidUtils.setLanguage(this, language); AndroidUtils.setLanguage(this, language, recreate);
} }
} }

View File

@ -45,7 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.ImportExportSharedPreferences; import nodomain.freeyourgadget.gadgetbridge.util.ImportExportSharedPreferences;
public class DbManagementActivity extends GBActivity { public class DbManagementActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(DbManagementActivity.class); private static final Logger LOG = LoggerFactory.getLogger(DbManagementActivity.class);
private static SharedPreferences sharedPrefs; private static SharedPreferences sharedPrefs;
private ImportExportSharedPreferences shared_file = new ImportExportSharedPreferences(); private ImportExportSharedPreferences shared_file = new ImportExportSharedPreferences();

View File

@ -53,7 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class DebugActivity extends GBActivity { public class DebugActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class); private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class);
private static final String EXTRA_REPLY = "reply"; private static final String EXTRA_REPLY = "reply";

View File

@ -69,7 +69,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY; import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY; import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY;
public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemClickListener { public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener {
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class); private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
private static final long SCAN_DURATION = 60000; // 60s private static final long SCAN_DURATION = 60000; // 60s

View File

@ -40,7 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton; import nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton;
public class ExternalPebbleJSActivity extends GBActivity { public class ExternalPebbleJSActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class);

View File

@ -51,7 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class FwAppInstallerActivity extends GBActivity implements InstallActivity { public class FwAppInstallerActivity extends AbstractGBActivity implements InstallActivity {
private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class); private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class);
private static final String ITEM_DETAILS = "details"; private static final String ITEM_DETAILS = "details";

View File

@ -1,80 +1,9 @@
/* 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; package nodomain.freeyourgadget.gadgetbridge.activities;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.LocaleList;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import java.util.Locale; import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; public interface GBActivity {
import nodomain.freeyourgadget.gadgetbridge.R; void setLanguage(Locale language, boolean recreate);
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; void setTheme(int themeId);
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class GBActivity extends AppCompatActivity {
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case GBApplication.ACTION_LANGUAGE_CHANGE:
setLanguage(GBApplication.getLanguage());
break;
case GBApplication.ACTION_QUIT:
finish();
break;
}
}
};
private void setLanguage(Locale language) {
AndroidUtils.setLanguage(this, language);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(GBApplication.ACTION_QUIT);
filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
if (GBApplication.isDarkThemeEnabled()) {
setTheme(R.style.GadgetbridgeThemeDark);
} else {
setTheme(R.style.GadgetbridgeTheme);
}
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
} }

View File

@ -26,7 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
public class VibrationActivity extends GBActivity { public class VibrationActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(VibrationActivity.class); private static final Logger LOG = LoggerFactory.getLogger(VibrationActivity.class);
private SeekBar seekBar; private SeekBar seekBar;

View File

@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity; import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
@ -46,7 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class MiBandPairingActivity extends GBActivity { public class MiBandPairingActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class); private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class);
private static final int REQ_CODE_USER_SETTINGS = 52; private static final int REQ_CODE_USER_SETTINGS = 52;

View File

@ -0,0 +1,31 @@
package nodomain.freeyourgadget.gadgetbridge.devices.no1f1;
import java.util.UUID;
public final class No1F1Constants {
public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("000033f1-0000-1000-8000-00805f9b34fb");
public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("000033f2-0000-1000-8000-00805f9b34fb");
public static final UUID UUID_SERVICE_NO1 = UUID.fromString("000055ff-0000-1000-8000-00805f9b34fb");
public static final byte CMD_DISPLAY_SETTINGS = (byte) 0xa0;
public static final byte CMD_FIRMWARE_VERSION = (byte) 0xa1;
public static final byte CMD_BATTERY = (byte) 0xa2;
public static final byte CMD_DATETIME = (byte) 0xa3;
public static final byte CMD_USER_DATA = (byte) 0xa9;
public static final byte CMD_ALARM = (byte) 0xab;
public static final byte CMD_FACTORY_RESET = (byte) 0xad;
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;
public static final byte CMD_HEARTRATE_SETTINGS = (byte) 0xd6;
public static final byte NOTIFICATION_HEADER = (byte) 0x01;
public static final byte NOTIFICATION_CALL = (byte) 0x02;
public static final byte NOTIFICATION_SMS = (byte) 0x03;
public static final byte NOTIFICATION_STOP = (byte) 0x04; // to stop showing incoming call
public static final byte ICON_QQ = (byte) 0x01;
public static final byte ICON_WECHAT = (byte) 0x02;
public static final byte ICON_ALARM = (byte) 0x04;
}

View File

@ -0,0 +1,140 @@
package nodomain.freeyourgadget.gadgetbridge.devices.no1f1;
import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelUuid;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class No1F1Coordinator extends AbstractDeviceCoordinator {
@NonNull
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public Collection<? extends ScanFilter> createBLEScanFilters() {
ParcelUuid hpService = new ParcelUuid(No1F1Constants.UUID_SERVICE_NO1);
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(hpService).build();
return Collections.singletonList(filter);
}
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
String name = candidate.getDevice().getName();
if (name != null && name.startsWith("X-RUN")) {
return DeviceType.NO1F1;
}
return DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.NO1F1;
}
@Override
public int getBondingStyle(GBDevice deviceCandidate) {
return BONDING_STYLE_NONE;
}
@Nullable
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Nullable
@Override
public Class<? extends Activity> getPrimaryActivity() {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return false;
}
@Override
public boolean supportsActivityTracking() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return false;
}
@Override
public String getManufacturer() {
return "NO1";
}
@Override
public boolean supportsAppsManagement() {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return false;
}
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
}
}

View File

@ -38,7 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity; import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@ -50,7 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class PebblePairingActivity extends GBActivity { public class PebblePairingActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(PebblePairingActivity.class); private static final Logger LOG = LoggerFactory.getLogger(PebblePairingActivity.class);
private TextView message; private TextView message;
private boolean isPairing; private boolean isPairing;

View File

@ -19,9 +19,9 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.os.Bundle; import android.os.Bundle;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
public class WeatherNotificationConfig extends GBActivity { public class WeatherNotificationConfig extends AbstractGBActivity {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);

View File

@ -61,6 +61,7 @@ public class GBDevice implements Parcelable {
private static final String DEVINFO_HW_VER = "HW: "; private static final String DEVINFO_HW_VER = "HW: ";
private static final String DEVINFO_FW_VER = "FW: "; private static final String DEVINFO_FW_VER = "FW: ";
private static final String DEVINFO_HR_VER = "HR: "; private static final String DEVINFO_HR_VER = "HR: ";
private static final String DEVINFO_GPS_VER = "GPS: ";
private static final String DEVINFO_ADDR = "ADDR: "; private static final String DEVINFO_ADDR = "ADDR: ";
private static final String DEVINFO_ADDR2 = "ADDR2: "; private static final String DEVINFO_ADDR2 = "ADDR2: ";
private String mName; private String mName;
@ -165,7 +166,7 @@ public class GBDevice implements Parcelable {
} }
/** /**
* Sets the second firmware version, typically the heart rate firmware version * Sets the second firmware version (HR or GPS or other component)
* @param firmwareVersion2 * @param firmwareVersion2
*/ */
public void setFirmwareVersion2(String firmwareVersion2) { public void setFirmwareVersion2(String firmwareVersion2) {
@ -445,7 +446,12 @@ public class GBDevice implements Parcelable {
result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion)); result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion));
} }
if (mFirmwareVersion2 != null) { if (mFirmwareVersion2 != null) {
result.add(new GenericItem(DEVINFO_HR_VER, mFirmwareVersion2)); // FIXME: thats ugly
if (mDeviceType == DeviceType.AMAZFITBIP) {
result.add(new GenericItem(DEVINFO_GPS_VER, mFirmwareVersion2));
} else {
result.add(new GenericItem(DEVINFO_HR_VER, mFirmwareVersion2));
}
} }
if (mAddress != null) { if (mAddress != null) {
result.add(new GenericItem(DEVINFO_ADDR, mAddress)); result.add(new GenericItem(DEVINFO_ADDR, mAddress));

View File

@ -37,6 +37,7 @@ public enum DeviceType {
LIVEVIEW(30, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled), 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), 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), MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
NO1F1(50, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled); TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled);
private final int key; private final int key;

View File

@ -229,7 +229,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
Intent shareIntent = new Intent(Intent.ACTION_SEND); Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*"); shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fullpath))); shareIntent.putExtra(Intent.EXTRA_STREAM, screenshotURI);
PendingIntent pendingShareIntent = PendingIntent.getActivity(context, 0, Intent.createChooser(shareIntent, "share screenshot"), PendingIntent pendingShareIntent = PendingIntent.getActivity(context, 0, Intent.createChooser(shareIntent, "share screenshot"),
PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent.FLAG_UPDATE_CURRENT);

View File

@ -189,7 +189,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
"net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED", "net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED",
"com.maxmpz.audioplayer.TPOS_SYNC", "com.maxmpz.audioplayer.TPOS_SYNC",
"com.maxmpz.audioplayer.STATUS_CHANGED", "com.maxmpz.audioplayer.STATUS_CHANGED",
"com.maxmpz.audioplayer.PLAYING_MODE_CHANGED"}; "com.maxmpz.audioplayer.PLAYING_MODE_CHANGED",
"com.spotify.music.metadatachanged",
"com.spotify.music.playbackstatechanged"
};
/** /**
* For testing! * For testing!

View File

@ -32,6 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSup
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport;
@ -125,6 +126,9 @@ public class DeviceSupportFactory {
case MAKIBESF68: case MAKIBESF68:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break; break;
case NO1F1:
deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
} }
if (deviceSupport != null) { if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext); deviceSupport.setContext(gbDevice, mBtAdapter, mContext);

View File

@ -85,6 +85,9 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLEDeviceSupport>
* You MUST call this method when the operation has finished, either * You MUST call this method when the operation has finished, either
* successfully or unsuccessfully. * successfully or unsuccessfully.
* *
* Subclasses must ensure that the {@link BtLEQueue queue's}'s gatt callback (set on the transaction builder by {@link #performInitialized(String)})
* is being unset, otherwise it will continue to receive events until another transaction is being executed by the queue.
*
* @throws IOException * @throws IOException
*/ */
protected void operationFinished() throws IOException { protected void operationFinished() throws IOException {

View File

@ -110,49 +110,49 @@ public class DeviceInfoProfile<T extends AbstractBTLEDeviceSupport> extends Abst
private void handleManufacturerName(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleManufacturerName(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String name = characteristic.getStringValue(0); String name = characteristic.getStringValue(0).trim();
deviceInfo.setManufacturerName(name); deviceInfo.setManufacturerName(name);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleModelNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleModelNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String modelNumber = characteristic.getStringValue(0); String modelNumber = characteristic.getStringValue(0).trim();
deviceInfo.setModelNumber(modelNumber); deviceInfo.setModelNumber(modelNumber);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleSerialNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleSerialNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String serialNumber = characteristic.getStringValue(0); String serialNumber = characteristic.getStringValue(0).trim();
deviceInfo.setSerialNumber(serialNumber); deviceInfo.setSerialNumber(serialNumber);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleHardwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleHardwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String hardwareRevision = characteristic.getStringValue(0); String hardwareRevision = characteristic.getStringValue(0).trim();
deviceInfo.setHardwareRevision(hardwareRevision); deviceInfo.setHardwareRevision(hardwareRevision);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleFirmwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleFirmwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String firmwareRevision = characteristic.getStringValue(0); String firmwareRevision = characteristic.getStringValue(0).trim();
deviceInfo.setFirmwareRevision(firmwareRevision); deviceInfo.setFirmwareRevision(firmwareRevision);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleSoftwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleSoftwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String softwareRevision = characteristic.getStringValue(0); String softwareRevision = characteristic.getStringValue(0).trim();
deviceInfo.setSoftwareRevision(softwareRevision); deviceInfo.setSoftwareRevision(softwareRevision);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleSystemId(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleSystemId(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String systemId = characteristic.getStringValue(0); String systemId = characteristic.getStringValue(0).trim();
deviceInfo.setSystemId(systemId); deviceInfo.setSystemId(systemId);
notify(createIntent(deviceInfo)); notify(createIntent(deviceInfo));
} }
private void handleRegulatoryCertificationData(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { private void handleRegulatoryCertificationData(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// TODO: regulatory certification data list not supported yet // TODO: regulatory certification data list not supported yet
// String regulatoryCertificationData = characteristic.getStringValue(0); // String regulatoryCertificationData = characteristic.getStringValue(0).trim();
// deviceInfo.setRegulatoryCertificationDataList(regulatoryCertificationData); // deviceInfo.setRegulatoryCertificationDataList(regulatoryCertificationData);
// notify(createIntent(deviceInfo)); // notify(createIntent(deviceInfo));
} }

View File

@ -89,6 +89,10 @@ public class AmazfitBipSupport extends MiBand2Support {
if (notificationSpec.type == NotificationType.GENERIC_SMS) { if (notificationSpec.type == NotificationType.GENERIC_SMS) {
alertCategory = AlertCategory.SMS; alertCategory = AlertCategory.SMS;
} }
// EMAIL icon does not work in FW 0.0.8.74, it did in 0.0.7.90
else if (notificationSpec.type == NotificationType.GENERIC_EMAIL) {
alertCategory = AlertCategory.Email;
}
NewAlert alert = new NewAlert(alertCategory, 1, message, customIconId); NewAlert alert = new NewAlert(alertCategory, 1, message, customIconId);
profile.newAlert(builder, alert); profile.newAlert(builder, alert);

View File

@ -40,7 +40,7 @@ class AmazfitBipTextNotificationStrategy extends Mi2TextNotificationStrategy {
@Override @Override
protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) { protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) {
if (simpleNotification != null && !StringUtils.isEmpty(simpleNotification.getMessage())) { if (simpleNotification != null) {
sendAlert(simpleNotification, builder); sendAlert(simpleNotification, builder);
} }
} }

View File

@ -16,34 +16,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.operations; package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.operations;
import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.widget.Toast;
import java.io.IOException; import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip.AmazfitBipFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip.AmazfitBipFWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2FirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class AmazfitBipUpdateFirmwareOperation extends UpdateFirmwareOperation { public class AmazfitBipUpdateFirmwareOperation extends UpdateFirmwareOperation {
public AmazfitBipUpdateFirmwareOperation(Uri uri, AmazfitBipSupport support) { public AmazfitBipUpdateFirmwareOperation(Uri uri, AmazfitBipSupport support) {
super(uri, support); super(uri, support);
} }
@Override
protected void doPerform() throws IOException {
AmazfitBipFWHelper mFwHelper = new AmazfitBipFWHelper(uri, getContext());
firmwareInfo = mFwHelper.getFirmwareInfo(); protected Mi2FirmwareInfo createFwInfo(Uri uri, Context context) throws IOException {
if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) { AmazfitBipFWHelper fwHelper = new AmazfitBipFWHelper(uri, getContext());
throw new IOException("Firmware is not compatible with the given device: " + getDevice().getAddress()); return fwHelper.getFirmwareInfo();
}
if (!sendFwInfo()) {
displayMessage(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR);
done();
}
//the firmware will be sent by the notification listener if the band confirms that the metadata are ok.
} }
} }

View File

@ -48,6 +48,7 @@ public abstract class AbstractMiBandOperation<T extends AbstractBTLEDeviceSuppor
try { try {
TransactionBuilder builder = performInitialized("reenabling disabled notifications"); TransactionBuilder builder = performInitialized("reenabling disabled notifications");
handleFinished(builder); handleFinished(builder);
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
builder.queue(getQueue()); builder.queue(getQueue());
} catch (IOException ex) { } catch (IOException ex) {
GB.toast(getContext(), "Error enabling Mi Band notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex); GB.toast(getContext(), "Error enabling Mi Band notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);

View File

@ -68,9 +68,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
@Override @Override
protected void doPerform() throws IOException { protected void doPerform() throws IOException {
MiBand2FWHelper mFwHelper = new MiBand2FWHelper(uri, getContext()); firmwareInfo = createFwInfo(uri, getContext());
firmwareInfo = mFwHelper.getFirmwareInfo();
if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) { if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) {
throw new IOException("Firmware is not compatible with the given device: " + getDevice().getAddress()); throw new IOException("Firmware is not compatible with the given device: " + getDevice().getAddress());
} }
@ -82,6 +80,11 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
//the firmware will be sent by the notification listener if the band confirms that the metadata are ok. //the firmware will be sent by the notification listener if the band confirms that the metadata are ok.
} }
protected Mi2FirmwareInfo createFwInfo(Uri uri, Context context) throws IOException {
MiBand2FWHelper fwHelper = new MiBand2FWHelper(uri, context);
return fwHelper.getFirmwareInfo();
}
protected void done() { protected void done() {
LOG.info("Operation done."); LOG.info("Operation done.");
operationFinished(); operationFinished();

View File

@ -0,0 +1,433 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Constants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import static org.apache.commons.lang3.math.NumberUtils.min;
public class No1F1Support extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(No1F1Support.class);
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
public BluetoothGattCharacteristic ctrlCharacteristic = null;
public BluetoothGattCharacteristic measureCharacteristic = null;
public No1F1Support() {
super(LOG);
addSupportedService(No1F1Constants.UUID_SERVICE_NO1);
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
measureCharacteristic = getCharacteristic(No1F1Constants.UUID_CHARACTERISTIC_MEASURE);
ctrlCharacteristic = getCharacteristic(No1F1Constants.UUID_CHARACTERISTIC_CONTROL);
builder.setGattCallback(this);
builder.notify(measureCharacteristic, true);
sendSettings(builder);
builder.write(ctrlCharacteristic, new byte[]{No1F1Constants.CMD_FIRMWARE_VERSION});
builder.write(ctrlCharacteristic, new byte[]{No1F1Constants.CMD_BATTERY});
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
LOG.info("Initialization Done");
return builder;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if (super.onCharacteristicChanged(gatt, characteristic)) {
return true;
}
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (data.length == 0)
return true;
switch (data[0]) {
case No1F1Constants.CMD_FIRMWARE_VERSION:
versionCmd.fwVersion = new String(Arrays.copyOfRange(data, 1, data.length));
handleGBDeviceEvent(versionCmd);
LOG.info("Firmware version is: " + versionCmd.fwVersion);
return true;
case No1F1Constants.CMD_BATTERY:
batteryCmd.level = data[1];
handleGBDeviceEvent(batteryCmd);
LOG.info("Battery level is: " + data[1]);
return true;
case No1F1Constants.CMD_DATETIME:
LOG.info("Time is set to: " + (data[1] * 256 + ((int) data[2] & 0xff)) + "-" + data[3] + "-" + data[4] + " " + data[5] + ":" + data[6] + ":" + data[7]);
return true;
case No1F1Constants.CMD_USER_DATA:
LOG.info("User data updated");
return true;
case No1F1Constants.CMD_NOTIFICATION:
case No1F1Constants.CMD_ICON:
case No1F1Constants.CMD_DEVICE_SETTINGS:
return true;
default:
LOG.warn("Unhandled characteristic change: " + characteristicUUID + " code: " + Arrays.toString(data));
return true;
}
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
switch (notificationSpec.type) {
case GENERIC_SMS:
showNotification(No1F1Constants.NOTIFICATION_SMS, notificationSpec.phoneNumber, notificationSpec.body);
setVibration(1, 3);
break;
default:
showIcon(No1F1Constants.ICON_WECHAT);
setVibration(1, 2);
break;
}
}
@Override
public void onDeleteNotification(int id) {
}
@Override
public void onSetTime() {
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
}
@Override
public void onSetCallState(CallSpec callSpec) {
if (callSpec.command == CallSpec.CALL_INCOMING) {
showNotification(No1F1Constants.NOTIFICATION_CALL, callSpec.name, callSpec.number);
setVibration(3, 5);
} else {
stopNotification();
setVibration(0, 0);
}
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
}
@Override
public void onEnableRealtimeSteps(boolean enable) {
}
@Override
public void onInstallApp(Uri uri) {
}
@Override
public void onAppInfoReq() {
}
@Override
public void onAppStart(UUID uuid, boolean start) {
}
@Override
public void onAppDelete(UUID uuid) {
}
@Override
public void onAppConfiguration(UUID appUuid, String config) {
}
@Override
public void onAppReorder(UUID[] uuids) {
}
@Override
public void onFetchActivityData() {
}
@Override
public void onReboot() {
try {
TransactionBuilder builder = performInitialized("clearNotification");
byte[] msg = new byte[]{
(byte) 0xad
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
} catch (IOException e) {
}
}
@Override
public void onHeartRateTest() {
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
}
@Override
public void onFindDevice(boolean start) {
if (start)
setVibration(3, 10);
else
setVibration(0, 0);
}
@Override
public void onSetConstantVibration(int integer) {
}
@Override
public void onScreenshotReq() {
}
@Override
public void onEnableHeartRateSleepSupport(boolean enable) {
}
@Override
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
}
@Override
public void onDeleteCalendarEvent(byte type, long id) {
}
@Override
public void onSendConfiguration(String config) {
}
@Override
public void onTestNewFunction() {
}
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
}
@Override
public boolean useAutoConnect() {
return true;
}
private void sendSettings(TransactionBuilder builder) {
// TODO Create custom settings page for changing hardcoded values
// set date and time
Calendar c = GregorianCalendar.getInstance();
byte[] datetimeBytes = new byte[]{
No1F1Constants.CMD_DATETIME,
(byte) ((c.get(Calendar.YEAR) / 256) & 0xff),
(byte) (c.get(Calendar.YEAR) % 256),
(byte) (c.get(Calendar.MONTH) + 1),
(byte) c.get(Calendar.DAY_OF_MONTH),
(byte) c.get(Calendar.HOUR_OF_DAY),
(byte) c.get(Calendar.MINUTE),
(byte) c.get(Calendar.SECOND)
};
builder.write(ctrlCharacteristic, datetimeBytes);
// set user data
ActivityUser activityUser = new ActivityUser();
byte[] userBytes = new byte[]{
No1F1Constants.CMD_USER_DATA,
0,
(byte) Math.round(activityUser.getHeightCm() * 0.43), // step length in cm
0,
(byte) activityUser.getWeightKg(),
5, // screen on time
0,
0,
(byte) (activityUser.getStepsGoal() / 256),
(byte) (activityUser.getStepsGoal() % 256),
1, // unknown
(byte) 0xff, // unknown
0,
(byte) activityUser.getAge(),
0
};
if (activityUser.getGender() == ActivityUser.GENDER_FEMALE)
userBytes[14] = 2; // female
else
userBytes[14] = 1; // male
builder.write(ctrlCharacteristic, userBytes);
// more settings
builder.write(ctrlCharacteristic, new byte[]{
No1F1Constants.CMD_DEVICE_SETTINGS,
0x00, // 1 - turns on inactivity alarm
0x3c,
0x02,
0x03,
0x01,
0x00
});
// display settings
builder.write(ctrlCharacteristic, new byte[]{
No1F1Constants.CMD_DISPLAY_SETTINGS,
0x01, // 1 - display distance in kilometers, 2 - in miles
0x01 // 1 - display 24-hour clock, 2 - for 12-hour with AM/PM
});
// heart rate measurement mode
builder.write(ctrlCharacteristic, new byte[]{
No1F1Constants.CMD_HEARTRATE_SETTINGS,
0x02, // 1 - static (measure for 15 seconds), 2 - realtime
});
// periodic heart rate measurement
builder.write(ctrlCharacteristic, new byte[]{
No1F1Constants.CMD_HEARTRATE_SETTINGS,
0x01,
0x02 // measure heart rate every 2 hours (0 to turn off)
});
}
private void setVibration(int duration, int count) {
try {
TransactionBuilder builder = performInitialized("vibrate");
byte[] msg = new byte[]{
No1F1Constants.CMD_ALARM,
0,
0,
0,
(byte) duration,
(byte) count,
2,
1
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
} catch (IOException e) {
}
}
private void showIcon(int iconId) {
try {
TransactionBuilder builder = performInitialized("showIcon");
byte[] msg = new byte[]{
No1F1Constants.CMD_ICON,
(byte) iconId
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
} catch (IOException e) {
}
}
private void showNotification(int type, String header, String body) {
try {
// TODO Add transliteration.
TransactionBuilder builder = performInitialized("showNotification");
int length;
byte[] bytes;
byte[] msg;
// send header
bytes = header.toString().getBytes("EUC-JP");
length = min(bytes.length, 18);
msg = new byte[length + 2];
msg[0] = No1F1Constants.CMD_NOTIFICATION;
msg[1] = No1F1Constants.NOTIFICATION_HEADER;
System.arraycopy(bytes, 0, msg, 2, length);
builder.write(ctrlCharacteristic, msg);
// send body
bytes = header.toString().getBytes("EUC-JP");
length = min(bytes.length, 18);
msg = new byte[length + 2];
msg[0] = No1F1Constants.CMD_NOTIFICATION;
msg[1] = (byte) type;
System.arraycopy(bytes, 0, msg, 2, length);
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
} catch (IOException e) {
}
}
private void stopNotification() {
try {
TransactionBuilder builder = performInitialized("clearNotification");
byte[] msg = new byte[]{
No1F1Constants.CMD_NOTIFICATION,
No1F1Constants.NOTIFICATION_STOP
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
} catch (IOException e) {
}
}
}

View File

@ -70,13 +70,20 @@ public class AndroidUtils {
} }
} }
public static void setLanguage(Activity activity, Locale language) { 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(); Configuration config = new Configuration();
config.setLocale(language); config.setLocale(language);
// FIXME: I have no idea what I am doing // FIXME: I have no idea what I am doing
activity.getBaseContext().getResources().updateConfiguration(config, activity.getBaseContext().getResources().getDisplayMetrics()); context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
activity.recreate();
} }
/** /**

View File

@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator;
import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.Device;
@ -192,6 +193,7 @@ public class DeviceHelper {
result.add(new VibratissimoCoordinator()); result.add(new VibratissimoCoordinator());
result.add(new LiveviewCoordinator()); result.add(new LiveviewCoordinator());
result.add(new HPlusCoordinator()); result.add(new HPlusCoordinator());
result.add(new No1F1Coordinator());
result.add(new MakibesF68Coordinator()); result.add(new MakibesF68Coordinator());
return result; return result;

View File

@ -9,6 +9,7 @@
<item android:maxLevel="130" android:drawable="@drawable/ic_device_default_disabled" /> <item android:maxLevel="130" android:drawable="@drawable/ic_device_default_disabled" />
<item android:maxLevel="140" android:drawable="@drawable/ic_device_hplus_disabled" /> <item android:maxLevel="140" android:drawable="@drawable/ic_device_hplus_disabled" />
<item android:maxLevel="141" android:drawable="@drawable/ic_device_hplus_disabled" /> <item android:maxLevel="141" android:drawable="@drawable/ic_device_hplus_disabled" />
<item android:maxLevel="150" android:drawable="@drawable/ic_device_hplus_disabled" />
<item android:maxLevel="199" android:drawable="@drawable/ic_launcher" /> <item android:maxLevel="199" android:drawable="@drawable/ic_launcher" />
<item android:maxLevel="201" android:drawable="@drawable/ic_device_pebble" /> <item android:maxLevel="201" android:drawable="@drawable/ic_device_pebble" />
@ -19,5 +20,6 @@
<item android:maxLevel="230" android:drawable="@drawable/ic_launcher" /> <item android:maxLevel="230" android:drawable="@drawable/ic_launcher" />
<item android:maxLevel="240" android:drawable="@drawable/ic_device_hplus" /> <item android:maxLevel="240" android:drawable="@drawable/ic_device_hplus" />
<item android:maxLevel="241" android:drawable="@drawable/ic_device_hplus" /> <item android:maxLevel="241" android:drawable="@drawable/ic_device_hplus" />
<item android:maxLevel="250" android:drawable="@drawable/ic_device_hplus" />
</level-list> </level-list>