From 32c03013cef93cc1c5c29d8b8e88e72eb0f50064 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 29 Aug 2017 23:12:28 +0200 Subject: [PATCH 01/17] Display fw2 as GPS for Amazfit Bip --- .../freeyourgadget/gadgetbridge/impl/GBDevice.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index f1c095b11..2444aabcc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -61,6 +61,7 @@ public class GBDevice implements Parcelable { private static final String DEVINFO_HW_VER = "HW: "; private static final String DEVINFO_FW_VER = "FW: "; 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_ADDR2 = "ADDR2: "; 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 */ public void setFirmwareVersion2(String firmwareVersion2) { @@ -445,7 +446,12 @@ public class GBDevice implements Parcelable { result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion)); } 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) { result.add(new GenericItem(DEVINFO_ADDR, mAddress)); From be147913c3db19ff4dd78863b1c000c14b138f95 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 29 Aug 2017 23:56:55 +0200 Subject: [PATCH 02/17] Add Liberapay donation button --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8e713ba50..a4fbd80ac 100644 --- a/README.md +++ b/README.md @@ -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 vendor's servers. + [Homepage](https://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) ## Download From e839a2c6a3a3243136945115f15238ed3f9af280 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 31 Aug 2017 11:50:26 +0200 Subject: [PATCH 03/17] Trim strings coming from DeviceInfoProfile (BLE). On the Bip strings have trailing zeroes. Putting this in the Database results in a "BLOB" --- .../profiles/deviceinfo/DeviceInfoProfile.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java index c0e46acea..587c8da71 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java @@ -110,49 +110,49 @@ public class DeviceInfoProfile extends Abst private void handleManufacturerName(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String name = characteristic.getStringValue(0); + String name = characteristic.getStringValue(0).trim(); deviceInfo.setManufacturerName(name); notify(createIntent(deviceInfo)); } private void handleModelNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String modelNumber = characteristic.getStringValue(0); + String modelNumber = characteristic.getStringValue(0).trim(); deviceInfo.setModelNumber(modelNumber); notify(createIntent(deviceInfo)); } private void handleSerialNumber(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String serialNumber = characteristic.getStringValue(0); + String serialNumber = characteristic.getStringValue(0).trim(); deviceInfo.setSerialNumber(serialNumber); notify(createIntent(deviceInfo)); } private void handleHardwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String hardwareRevision = characteristic.getStringValue(0); + String hardwareRevision = characteristic.getStringValue(0).trim(); deviceInfo.setHardwareRevision(hardwareRevision); notify(createIntent(deviceInfo)); } private void handleFirmwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String firmwareRevision = characteristic.getStringValue(0); + String firmwareRevision = characteristic.getStringValue(0).trim(); deviceInfo.setFirmwareRevision(firmwareRevision); notify(createIntent(deviceInfo)); } private void handleSoftwareRevision(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String softwareRevision = characteristic.getStringValue(0); + String softwareRevision = characteristic.getStringValue(0).trim(); deviceInfo.setSoftwareRevision(softwareRevision); notify(createIntent(deviceInfo)); } private void handleSystemId(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - String systemId = characteristic.getStringValue(0); + String systemId = characteristic.getStringValue(0).trim(); deviceInfo.setSystemId(systemId); notify(createIntent(deviceInfo)); } private void handleRegulatoryCertificationData(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { // TODO: regulatory certification data list not supported yet -// String regulatoryCertificationData = characteristic.getStringValue(0); +// String regulatoryCertificationData = characteristic.getStringValue(0).trim(); // deviceInfo.setRegulatoryCertificationDataList(regulatoryCertificationData); // notify(createIntent(deviceInfo)); } From f5b8fada75842ee6795f62fad71e16b38885d271 Mon Sep 17 00:00:00 2001 From: protomors Date: Tue, 29 Aug 2017 18:28:30 +0300 Subject: [PATCH 04/17] Initial NO.1 F1 support. Works: connecting, writing user data, reading firmware version and battery charge, finding device. --- .../devices/no1f1/No1F1Constants.java | 17 + .../devices/no1f1/No1F1Coordinator.java | 140 ++++++++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 4 + .../service/devices/no1f1/No1F1Support.java | 299 ++++++++++++++++++ .../gadgetbridge/util/DeviceHelper.java | 2 + 6 files changed, 463 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java new file mode 100644 index 000000000..a4297cc8f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java @@ -0,0 +1,17 @@ +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_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; + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java new file mode 100644 index 000000000..bb51683a6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java @@ -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 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 getPairingActivity() { + return null; + } + + @Nullable + @Override + public Class getPrimaryActivity() { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider 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 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 { + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 209666250..d543f913d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -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), + 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); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 86a8f0a74..f17a5eec8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -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.amazfitbip.AmazfitBipSupport; 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.vibratissimo.VibratissimoSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; @@ -125,6 +126,9 @@ public class DeviceSupportFactory { case MAKIBESF68: deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case NO1F1: + deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java new file mode 100644 index 000000000..4f9823c51 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -0,0 +1,299 @@ +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; + +public class No1F1Support extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(No1F1Support.class); + + public BluetoothGattCharacteristic ctrlCharacteristic = null; + public BluetoothGattCharacteristic measureCharacteristic = null; + + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + + 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); + + builder.write(ctrlCharacteristic, new byte[]{No1F1Constants.CMD_FIRMWARE_VERSION}); + builder.write(ctrlCharacteristic, new byte[]{No1F1Constants.CMD_BATTERY}); + + ActivityUser activityUser = new ActivityUser(); + byte[] msg = new byte[]{ + No1F1Constants.CMD_USER_DATA, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + msg[2]=(byte) Math.round(activityUser.getHeightCm() * 0.43); // step length in cm + msg[4]=(byte) activityUser.getWeightKg(); + msg[5]=5; // screen on time + msg[8]=(byte) (activityUser.getStepsGoal()/256); + msg[9]=(byte) (activityUser.getStepsGoal()%256); + msg[10]=1; // unknown + msg[11]=(byte)0xff; // unknown + msg[13]=(byte) activityUser.getAge(); + if (activityUser.getGender() == ActivityUser.GENDER_FEMALE) + msg[14]=2; // female + else + msg[14]=1; // male + + builder.write(ctrlCharacteristic, msg); + + 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; + default: + LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + Arrays.toString(data)); + return true; + } + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + + } + + @Override + public void onDeleteNotification(int id) { + + } + + @Override + public void onSetTime() { + try { + TransactionBuilder builder = performInitialized("time"); + Calendar c = GregorianCalendar.getInstance(); + int year = c.get(Calendar.YEAR); + byte[] msg = new byte[]{ + No1F1Constants.CMD_DATETIME, + (byte) ((year / 256) & 0xff), + (byte) (year % 256), + (byte) (c.get(Calendar.MONTH)+1), + (byte) c.get(Calendar.DAY_OF_MONTH), + (byte) c.get(Calendar.HOUR), + (byte) c.get(Calendar.MINUTE), + (byte) c.get(Calendar.SECOND) + }; + builder.write(ctrlCharacteristic, msg); + performConnected(builder.getTransaction()); + }catch(IOException e){ + + } + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onSetCallState(CallSpec callSpec) { + + } + + @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() { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + + } + + @Override + public void onFindDevice(boolean start) { + try { + TransactionBuilder builder = performInitialized("findMe"); + byte[] msg = new byte[]{No1F1Constants.CMD_ALARM, 0, 0, 0, 0, 0, 2, 1}; + if (start) + { + msg[4]=1; // vibrate 10 times with duration of 1 second + msg[5]=10; // sending zeroes stops vibration immediately + } + builder.write(ctrlCharacteristic, msg); + performConnected(builder.getTransaction()); + } catch (IOException e) { + } + } + + @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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index fa0d99608..7d18e3500 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; 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.vibratissimo.VibratissimoCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.Device; @@ -192,6 +193,7 @@ public class DeviceHelper { result.add(new VibratissimoCoordinator()); result.add(new LiveviewCoordinator()); result.add(new HPlusCoordinator()); + result.add(new No1F1Coordinator()); result.add(new MakibesF68Coordinator()); return result; From 259fc87b68c6bdfc5c8bed6fdc5a68f1e9ff43a2 Mon Sep 17 00:00:00 2001 From: protomors Date: Thu, 31 Aug 2017 20:36:47 +0300 Subject: [PATCH 05/17] Added device icon in resources. --- app/src/main/res/drawable/level_list_device.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/drawable/level_list_device.xml b/app/src/main/res/drawable/level_list_device.xml index 9347fd997..eb68abe6e 100644 --- a/app/src/main/res/drawable/level_list_device.xml +++ b/app/src/main/res/drawable/level_list_device.xml @@ -9,6 +9,7 @@ + @@ -19,5 +20,6 @@ + \ No newline at end of file From f8473ac42df4f2dd871b02e3275e3567900bc33b Mon Sep 17 00:00:00 2001 From: protomors Date: Fri, 1 Sep 2017 20:00:47 +0300 Subject: [PATCH 06/17] Initial notifications support and some refactoring. --- .../devices/no1f1/No1F1Constants.java | 10 + .../service/devices/no1f1/No1F1Support.java | 211 +++++++++++++----- 2 files changed, 162 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java index a4297cc8f..c0a34524f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java @@ -13,5 +13,15 @@ public final class No1F1Constants { 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_NOTIFICATION = (byte) 0xc1; + public static final byte CMD_ICON = (byte) 0xc3; + 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; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index 4f9823c51..af4f453fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -30,14 +30,14 @@ 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); - - public BluetoothGattCharacteristic ctrlCharacteristic = null; - public BluetoothGattCharacteristic measureCharacteristic = null; - private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + public BluetoothGattCharacteristic ctrlCharacteristic = null; + public BluetoothGattCharacteristic measureCharacteristic = null; public No1F1Support() { super(LOG); @@ -57,29 +57,11 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { 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}); - ActivityUser activityUser = new ActivityUser(); - byte[] msg = new byte[]{ - No1F1Constants.CMD_USER_DATA, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - msg[2]=(byte) Math.round(activityUser.getHeightCm() * 0.43); // step length in cm - msg[4]=(byte) activityUser.getWeightKg(); - msg[5]=5; // screen on time - msg[8]=(byte) (activityUser.getStepsGoal()/256); - msg[9]=(byte) (activityUser.getStepsGoal()%256); - msg[10]=1; // unknown - msg[11]=(byte)0xff; // unknown - msg[13]=(byte) activityUser.getAge(); - if (activityUser.getGender() == ActivityUser.GENDER_FEMALE) - msg[14]=2; // female - else - msg[14]=1; // male - - builder.write(ctrlCharacteristic, msg); - gbDevice.setState(GBDevice.State.INITIALIZED); gbDevice.sendDeviceUpdateIntent(getContext()); @@ -102,7 +84,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { switch (data[0]) { case No1F1Constants.CMD_FIRMWARE_VERSION: - versionCmd.fwVersion=new String(Arrays.copyOfRange(data,1,data.length)); + versionCmd.fwVersion = new String(Arrays.copyOfRange(data, 1, data.length)); handleGBDeviceEvent(versionCmd); LOG.info("Firmware version is: " + versionCmd.fwVersion); return true; @@ -112,20 +94,32 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { 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]); + 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: + return true; default: - LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + Arrays.toString(data)); + 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 @@ -135,25 +129,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { @Override public void onSetTime() { - try { - TransactionBuilder builder = performInitialized("time"); - Calendar c = GregorianCalendar.getInstance(); - int year = c.get(Calendar.YEAR); - byte[] msg = new byte[]{ - No1F1Constants.CMD_DATETIME, - (byte) ((year / 256) & 0xff), - (byte) (year % 256), - (byte) (c.get(Calendar.MONTH)+1), - (byte) c.get(Calendar.DAY_OF_MONTH), - (byte) c.get(Calendar.HOUR), - (byte) c.get(Calendar.MINUTE), - (byte) c.get(Calendar.SECOND) - }; - builder.write(ctrlCharacteristic, msg); - performConnected(builder.getTransaction()); - }catch(IOException e){ - } } @Override @@ -163,7 +139,13 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { @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 @@ -238,18 +220,10 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { @Override public void onFindDevice(boolean start) { - try { - TransactionBuilder builder = performInitialized("findMe"); - byte[] msg = new byte[]{No1F1Constants.CMD_ALARM, 0, 0, 0, 0, 0, 2, 1}; - if (start) - { - msg[4]=1; // vibrate 10 times with duration of 1 second - msg[5]=10; // sending zeroes stops vibration immediately - } - builder.write(ctrlCharacteristic, msg); - performConnected(builder.getTransaction()); - } catch (IOException e) { - } + if (start) + setVibration(3, 10); + else + setVibration(0, 0); } @Override @@ -296,4 +270,123 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { 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), + (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); + } + + 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) { + } + } } From c97136e4fe5cacd2cdb06469d1c66922ef6150bb Mon Sep 17 00:00:00 2001 From: protomors Date: Fri, 1 Sep 2017 20:32:25 +0300 Subject: [PATCH 07/17] Fixed wrong parameter. Time is set in 24-hour format. --- .../gadgetbridge/service/devices/no1f1/No1F1Support.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index af4f453fd..43e9b02ec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -282,7 +282,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { (byte) (c.get(Calendar.YEAR) % 256), (byte) (c.get(Calendar.MONTH) + 1), (byte) c.get(Calendar.DAY_OF_MONTH), - (byte) c.get(Calendar.HOUR), + (byte) c.get(Calendar.HOUR_OF_DAY), (byte) c.get(Calendar.MINUTE), (byte) c.get(Calendar.SECOND) }; From e7fff32fb88acd8d802740ac6720f018ab64e9ea Mon Sep 17 00:00:00 2001 From: protomors Date: Fri, 1 Sep 2017 21:12:09 +0300 Subject: [PATCH 08/17] Send more settings to device (with hardcoded values, for now). Now bracelet should work correctly without ever connecting to native app. --- .../devices/no1f1/No1F1Constants.java | 4 ++ .../service/devices/no1f1/No1F1Support.java | 43 ++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java index c0a34524f..e839955e0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java @@ -8,13 +8,17 @@ public final class No1F1Constants { 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; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index 43e9b02ec..4d4e8f293 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -101,6 +101,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { 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)); @@ -205,7 +206,15 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { @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 @@ -311,7 +320,39 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { 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) { From eaf7b76715d6606823f42ab0a84ebaff03d29887 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 1 Sep 2017 23:47:03 +0200 Subject: [PATCH 09/17] Pebble: Try to support spotify untested #704 --- .../gadgetbridge/service/DeviceCommunicationService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 067febb52..73eacc58f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -188,7 +188,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere "net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED", "com.maxmpz.audioplayer.TPOS_SYNC", "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! From c91e14f6449a5cbcba3ae146997c524e9b0e986b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 2 Sep 2017 21:45:56 +0200 Subject: [PATCH 10/17] Bip: some code deduplication --- .../AmazfitBipUpdateFirmwareOperation.java | 20 +++++-------------- .../operations/UpdateFirmwareOperation.java | 9 ++++++--- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java index a27c7ef08..48317d550 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipUpdateFirmwareOperation.java @@ -16,34 +16,24 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.operations; +import android.content.Context; import android.net.Uri; -import android.widget.Toast; import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip.AmazfitBipFWHelper; 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.util.GB; public class AmazfitBipUpdateFirmwareOperation extends UpdateFirmwareOperation { public AmazfitBipUpdateFirmwareOperation(Uri uri, AmazfitBipSupport support) { super(uri, support); } - @Override - protected void doPerform() throws IOException { - AmazfitBipFWHelper mFwHelper = new AmazfitBipFWHelper(uri, getContext()); - firmwareInfo = mFwHelper.getFirmwareInfo(); - if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) { - throw new IOException("Firmware is not compatible with the given device: " + getDevice().getAddress()); - } - - 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. + protected Mi2FirmwareInfo createFwInfo(Uri uri, Context context) throws IOException { + AmazfitBipFWHelper fwHelper = new AmazfitBipFWHelper(uri, getContext()); + return fwHelper.getFirmwareInfo(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java index fd5793e8d..b1d2e4bf1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java @@ -68,9 +68,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation { @Override protected void doPerform() throws IOException { - MiBand2FWHelper mFwHelper = new MiBand2FWHelper(uri, getContext()); - - firmwareInfo = mFwHelper.getFirmwareInfo(); + firmwareInfo = createFwInfo(uri, getContext()); if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) { 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. } + protected Mi2FirmwareInfo createFwInfo(Uri uri, Context context) throws IOException { + MiBand2FWHelper fwHelper = new MiBand2FWHelper(uri, context); + return fwHelper.getFirmwareInfo(); + } + protected void done() { LOG.info("Operation done."); operationFinished(); From c468e7f5215d99aa4d9f97f18b9fb7ee3a5d9f94 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 2 Sep 2017 22:45:21 +0200 Subject: [PATCH 11/17] Mi Bands+Bip: when an operation finishes, unset the gatt callback on the BtLEQeue Otherwise it will continue to receive events until another transaction is being executed. --- .../gadgetbridge/service/btle/AbstractBTLEOperation.java | 3 +++ .../devices/miband/operations/AbstractMiBandOperation.java | 1 + 2 files changed, 4 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java index 1c64f025c..3b9e847ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java @@ -85,6 +85,9 @@ public abstract class AbstractBTLEOperation * You MUST call this method when the operation has finished, either * 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 */ protected void operationFinished() throws IOException { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java index 2c1180e34..4dacf9fb0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java @@ -48,6 +48,7 @@ public abstract class AbstractMiBandOperation Date: Sat, 2 Sep 2017 23:24:52 +0200 Subject: [PATCH 12/17] Amazfit Bip: Use AlertCategory.Email for email Somehow the custom icon for email no longer works with never firmwares --- .../service/devices/amazfitbip/AmazfitBipSupport.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java index fe23fa7c2..23b2f37bb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java @@ -89,6 +89,10 @@ public class AmazfitBipSupport extends MiBand2Support { if (notificationSpec.type == NotificationType.GENERIC_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); profile.newAlert(builder, alert); From 962720145e3678f5ffd17ea6c4a9e277a665b144 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Sep 2017 01:02:31 +0200 Subject: [PATCH 13/17] Fix custom language being lost for newly created activities So we do need to set the language both on change and onCreate() For some reason, the title bar of the SettingsActivity is not updated on recreate(). Closes #787 --- .../gadgetbridge/GBApplication.java | 15 ++- .../activities/AbstractGBActivity.java | 101 ++++++++++++++++++ .../AbstractGBFragmentActivity.java | 2 +- .../activities/AbstractSettingsActivity.java | 15 +-- .../gadgetbridge/activities/AlarmDetails.java | 2 +- .../activities/AndroidPairingActivity.java | 2 +- .../activities/AppBlacklistActivity.java | 2 +- .../activities/CalBlacklistActivity.java | 2 +- .../activities/ConfigureAlarms.java | 2 +- .../activities/ControlCenterv2.java | 16 ++- .../activities/DbManagementActivity.java | 2 +- .../activities/DebugActivity.java | 2 +- .../activities/DiscoveryActivity.java | 2 +- .../activities/ExternalPebbleJSActivity.java | 2 +- .../activities/FwAppInstallerActivity.java | 2 +- .../gadgetbridge/activities/GBActivity.java | 77 +------------ .../activities/VibrationActivity.java | 2 +- .../devices/miband/MiBandPairingActivity.java | 4 +- .../devices/pebble/PebblePairingActivity.java | 4 +- .../WeatherNotificationConfig.java | 4 +- .../gadgetbridge/util/AndroidUtils.java | 13 ++- 21 files changed, 154 insertions(+), 119 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 1fc6f8a65..7ee81ed53 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -57,6 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.service.NotificationCollectorMonitorService; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; @@ -532,11 +533,11 @@ public class GBApplication extends Application { } else { language = new Locale(lang); } - Configuration config = new Configuration(); - config.setLocale(language); + updateLanguage(language); + } - // FIXME: I have no idea what I am doing - context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); + public static void updateLanguage(Locale locale) { + AndroidUtils.setLanguage(context, locale); Intent intent = new Intent(); intent.setAction(ACTION_LANGUAGE_CHANGE); @@ -558,6 +559,12 @@ public class GBApplication extends Application { return typedValue.data; } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateLanguage(getLanguage()); + } + public static int getBackgroundColor(Context context) { TypedValue typedValue = new TypedValue(); Resources.Theme theme = context.getTheme(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java new file mode 100644 index 000000000..3ee9e20dc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java @@ -0,0 +1,101 @@ +/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.activities; + + +import android.app.Activity; +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 nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + + +public 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(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java index 087954de7..2390aedff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java @@ -33,7 +33,7 @@ import android.support.v4.app.FragmentPagerAdapter; * * @see AbstractGBFragment */ -public abstract class AbstractGBFragmentActivity extends GBActivity { +public abstract class AbstractGBFragmentActivity extends AbstractGBActivity { /** * The {@link android.support.v4.view.PagerAdapter} that will provide * fragments for each of the sections. We use a diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index 14d37112a..a1ca99131 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -37,7 +37,6 @@ import org.slf4j.LoggerFactory; import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; 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 * 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); @@ -56,7 +55,7 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi String action = intent.getAction(); switch (action) { case GBApplication.ACTION_LANGUAGE_CHANGE: - setLanguage(GBApplication.getLanguage()); + setLanguage(GBApplication.getLanguage(), true); break; case GBApplication.ACTION_QUIT: finish(); @@ -129,11 +128,7 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi @Override protected void onCreate(Bundle savedInstanceState) { - if (GBApplication.isDarkThemeEnabled()) { - setTheme(R.style.GadgetbridgeThemeDark); - } else { - setTheme(R.style.GadgetbridgeTheme); - } + AbstractGBActivity.init(this); IntentFilter filterLocal = new IntentFilter(); filterLocal.addAction(GBApplication.ACTION_QUIT); @@ -215,7 +210,7 @@ public abstract class AbstractSettingsActivity extends AppCompatPreferenceActivi return super.onOptionsItemSelected(item); } - private void setLanguage(Locale language) { - AndroidUtils.setLanguage(this, language); + public void setLanguage(Locale language, boolean recreate) { + AndroidUtils.setLanguage(this, language, recreate); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java index 543673de3..d0cfa085d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -31,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; -public class AlarmDetails extends GBActivity { +public class AlarmDetails extends AbstractGBActivity { private GBAlarm alarm; private TimePicker timePicker; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java index f26dd3b04..a2c39bbcf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java @@ -20,7 +20,7 @@ import android.os.Bundle; import nodomain.freeyourgadget.gadgetbridge.R; -public class AndroidPairingActivity extends GBActivity { +public class AndroidPairingActivity extends AbstractGBActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index 5c3c10146..20ea96cc9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -31,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; 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 AppBlacklistAdapter appBlacklistAdapter; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java index 15307a32c..54e4ae15e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java @@ -47,7 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class CalBlacklistActivity extends GBActivity { +public class CalBlacklistActivity extends AbstractGBActivity { private final String[] EVENT_PROJECTION = new String[]{ CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index 584ac07da..3528c82be 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -38,7 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; 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; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java index 7e889efa1..377663d83 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java @@ -62,9 +62,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; 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 - implements NavigationView.OnNavigationItemSelectedListener { + implements NavigationView.OnNavigationItemSelectedListener, GBActivity { //needed for KK compatibility static { @@ -84,7 +84,7 @@ public class ControlCenterv2 extends AppCompatActivity String action = intent.getAction(); switch (action) { case GBApplication.ACTION_LANGUAGE_CHANGE: - setLanguage(GBApplication.getLanguage()); + setLanguage(GBApplication.getLanguage(), true); break; case GBApplication.ACTION_QUIT: finish(); @@ -98,11 +98,7 @@ public class ControlCenterv2 extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { - if (GBApplication.isDarkThemeEnabled()) { - setTheme(R.style.GadgetbridgeThemeDark_NoActionBar); - } else { - setTheme(R.style.GadgetbridgeTheme_NoActionBar); - } + AbstractGBActivity.init(this, AbstractGBActivity.NO_ACTIONBAR); super.onCreate(savedInstanceState); setContentView(R.layout.activity_controlcenterv2); @@ -315,7 +311,7 @@ public class ControlCenterv2 extends AppCompatActivity ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0); } - private void setLanguage(Locale language) { - AndroidUtils.setLanguage(this, language); + public void setLanguage(Locale language, boolean recreate) { + AndroidUtils.setLanguage(this, language, recreate); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java index 2085f7615..782f43a24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java @@ -45,7 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; 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 SharedPreferences sharedPrefs; private ImportExportSharedPreferences shared_file = new ImportExportSharedPreferences(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index a88df172e..03e3cecdf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -53,7 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; 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 String EXTRA_REPLY = "reply"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index 6c80cf76a..b7af301cf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -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.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 long SCAN_DURATION = 60000; // 60s diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 094639856..07befc2ea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -64,7 +64,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class ExternalPebbleJSActivity extends GBActivity { +public class ExternalPebbleJSActivity extends AbstractGBActivity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java index f1f5ac229..28b57e464 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java @@ -51,7 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; 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 String ITEM_DETAILS = "details"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java index 09246d6dc..7600c8f66 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java @@ -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 . */ 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 nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; -import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; +public interface GBActivity { + void setLanguage(Locale language, boolean recreate); + void setTheme(int themeId); - -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(); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java index ea92116a6..076d8d1c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java @@ -26,7 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; 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 SeekBar seekBar; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index deed2c2e0..4e49ae911 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2; 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.impl.GBDevice; 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.Prefs; -public class MiBandPairingActivity extends GBActivity { +public class MiBandPairingActivity extends AbstractGBActivity { private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class); private static final int REQ_CODE_USER_SETTINGS = 52; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java index f7823a01e..30d0b6d39 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java @@ -38,7 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2; 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.devices.DeviceCoordinator; 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.GB; -public class PebblePairingActivity extends GBActivity { +public class PebblePairingActivity extends AbstractGBActivity { private static final Logger LOG = LoggerFactory.getLogger(PebblePairingActivity.class); private TextView message; private boolean isPairing; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java index d7caa1e8a..93e4dcb2c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java @@ -19,9 +19,9 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.os.Bundle; 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 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index a3258b19f..06bafd277 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -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(); config.setLocale(language); // FIXME: I have no idea what I am doing - activity.getBaseContext().getResources().updateConfiguration(config, activity.getBaseContext().getResources().getDisplayMetrics()); - activity.recreate(); + context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); } /** From 24797c7dd7b2a0c648be79aa57ce2d96d0e77d89 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Sep 2017 10:48:14 +0200 Subject: [PATCH 14/17] Make AbstractGBActivity abstract :-) --- .../gadgetbridge/activities/AbstractGBActivity.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java index 3ee9e20dc..475031e23 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java @@ -17,14 +17,11 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; 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; @@ -32,12 +29,10 @@ import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class AbstractGBActivity extends AppCompatActivity implements GBActivity { +public abstract class AbstractGBActivity extends AppCompatActivity implements GBActivity { public static final int NONE = 0; public static final int NO_ACTIONBAR = 1; From a63dc4a018337768f02bc4e0f332629838ba9bc0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Sep 2017 14:11:51 +0200 Subject: [PATCH 15/17] Pebble: fix taking screenshots on Android 8.0 Closes #790 --- .../gadgetbridge/service/AbstractDeviceSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index a8ae4616a..24e014b9e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -229,7 +229,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { Intent shareIntent = new Intent(Intent.ACTION_SEND); 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.FLAG_UPDATE_CURRENT); From 45263d08d53359d263341010946ae91b6399d2e1 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Sep 2017 14:20:40 +0200 Subject: [PATCH 16/17] bump version, update CHANGELOG (not yet release time) --- CHANGELOG.md | 9 +++++++++ app/build.gradle | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 784d2e82a..2802fdf1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ### Changelog +#### Version 0.21.0 (next) +* Initial NO.1 F1 support +* Amazfit Bip: Display GPS firmware version +* Amazfit Bip: Fix E-Mail notifications +* 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 * Amazfit Bip: Various fixes regarding weather, add condition string support for FW 0.0.8.74 * Amazfit Bip: enable caller display in later firmwares diff --git a/app/build.gradle b/app/build.gradle index 06027cc0f..a9dff3d8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { targetSdkVersion 25 // note: always bump BOTH versionCode and versionName! - versionName "0.20.2" - versionCode 100 + versionName "0.21.0" + versionCode 101 vectorDrawables.useSupportLibrary = true } buildTypes { From cfc310692fe0f865e4117681c2d330ebca43e649 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Sep 2017 14:37:32 +0200 Subject: [PATCH 17/17] Amazfit Bip: Fix call notification with unknown caller --- CHANGELOG.md | 1 + .../devices/amazfitbip/AmazfitBipTextNotificationStrategy.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2802fdf1c..4656ac9f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * 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 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java index d7c9fac61..866f2f30b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java @@ -40,7 +40,7 @@ class AmazfitBipTextNotificationStrategy extends Mi2TextNotificationStrategy { @Override protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) { - if (simpleNotification != null && !StringUtils.isEmpty(simpleNotification.getMessage())) { + if (simpleNotification != null) { sendAlert(simpleNotification, builder); } }