mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 12:26:48 +01:00
Bangle.js: Fetch activity data
This commit is contained in:
parent
f97250d46b
commit
a95820d09e
@ -43,7 +43,7 @@ public class GBDaoGenerator {
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final Schema schema = new Schema(50, MAIN_PACKAGE + ".entities");
|
||||
final Schema schema = new Schema(51, MAIN_PACKAGE + ".entities");
|
||||
|
||||
Entity userAttributes = addUserAttributes(schema);
|
||||
Entity user = addUserInfo(schema, userAttributes);
|
||||
@ -613,6 +613,7 @@ public class GBDaoGenerator {
|
||||
Entity activitySample = addEntity(schema, "BangleJSActivitySample");
|
||||
activitySample.implementsSerializable();
|
||||
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
addHeartRateProperties(activitySample);
|
||||
|
@ -0,0 +1,38 @@
|
||||
/* Copyright (C) 2023 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.database.schema;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BangleJSActivitySampleDao;
|
||||
|
||||
public class GadgetbridgeUpdate_51 implements DBUpdateScript {
|
||||
@Override
|
||||
public void upgradeSchema(final SQLiteDatabase db) {
|
||||
if (!DBHelper.existsColumn(BangleJSActivitySampleDao.TABLENAME, BangleJSActivitySampleDao.Properties.RawIntensity.columnName, db)) {
|
||||
final String sqlAddColumnRawIntensity = "ALTER TABLE " + BangleJSActivitySampleDao.TABLENAME + " ADD COLUMN "
|
||||
+ BangleJSActivitySampleDao.Properties.RawIntensity.columnName + " INTEGER DEFAULT -1 NOT NULL";
|
||||
db.execSQL(sqlAddColumnRawIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downgradeSchema(final SQLiteDatabase db) {
|
||||
}
|
||||
}
|
@ -25,4 +25,8 @@ public final class BangleJSConstants {
|
||||
public static final UUID UUID_CHARACTERISTIC_NORDIC_UART_TX = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e");
|
||||
public static final UUID UUID_CHARACTERISTIC_NORDIC_UART_RX = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e");
|
||||
|
||||
public static final String PREF_BANGLEJS_ACTIVITY_FULL_SYNC_TRIGGER = "pref_banglejs_activity_full_sync_trigger";
|
||||
public static final String PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STATUS = "pref_banglejs_activity_full_sync_status";
|
||||
public static final String PREF_BANGLEJS_ACTIVITY_FULL_SYNC_START = "pref_banglejs_activity_full_sync_start";
|
||||
public static final String PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STOP = "pref_banglejs_activity_full_sync_stop";
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
@ -111,7 +112,7 @@ public class BangleJSCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -188,6 +189,7 @@ public class BangleJSCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificSettings(final GBDevice device) {
|
||||
final List<Integer> settings = new ArrayList<>();
|
||||
|
||||
@ -205,6 +207,8 @@ public class BangleJSCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
if (BuildConfig.INTERNET_ACCESS)
|
||||
settings.add(R.xml.devicesettings_device_internet_access);
|
||||
|
||||
settings.add(R.xml.devicesettings_banglejs_activity);
|
||||
|
||||
settings.add(R.xml.devicesettings_header_developer);
|
||||
settings.add(R.xml.devicesettings_banglejs_apploader);
|
||||
settings.add(R.xml.devicesettings_device_intents);
|
||||
@ -212,6 +216,11 @@ public class BangleJSCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
return ArrayUtils.toPrimitive(settings.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new BangleJSSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNavigation() {
|
||||
return true;
|
||||
|
@ -19,18 +19,23 @@ package nodomain.freeyourgadget.gadgetbridge.devices.banglejs;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.dao.AbstractDao;
|
||||
import de.greenrobot.dao.Property;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BangleJSActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BangleJSActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.ID115ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.ID115ActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
|
||||
public class BangleJSSampleProvider extends AbstractSampleProvider<BangleJSActivitySample> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BangleJSSampleProvider.class);
|
||||
|
||||
public BangleJSSampleProvider(GBDevice device, DaoSession session) {
|
||||
super(device, session);
|
||||
}
|
||||
@ -77,11 +82,54 @@ public class BangleJSSampleProvider extends AbstractSampleProvider<BangleJSActiv
|
||||
|
||||
@Override
|
||||
public float normalizeIntensity(int rawIntensity) {
|
||||
return rawIntensity;
|
||||
return rawIntensity / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BangleJSActivitySample createActivitySample() {
|
||||
return new BangleJSActivitySample();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upserts a sample in the database, avoiding duplicated samples if a sample already exists in a
|
||||
* close timestamp (within 2 minutes);
|
||||
*/
|
||||
public void upsertSample(final BangleJSActivitySample sample) {
|
||||
final List<BangleJSActivitySample> nearSamples = getGBActivitySamples(
|
||||
sample.getTimestamp() - 60 * 2,
|
||||
sample.getTimestamp() + 60 * 2,
|
||||
normalizeType(sample.getRawKind())
|
||||
);
|
||||
|
||||
if (nearSamples.isEmpty()) {
|
||||
// No nearest sample, just insert
|
||||
LOG.debug("No duplicate found at {}, inserting", sample.getTimestamp());
|
||||
addGBActivitySample(sample);
|
||||
return;
|
||||
}
|
||||
|
||||
BangleJSActivitySample nearestSample = nearSamples.get(0);
|
||||
|
||||
for (final BangleJSActivitySample s : nearSamples) {
|
||||
final int curDist = Math.abs(nearestSample.getTimestamp() - s.getTimestamp());
|
||||
final int newDist = Math.abs(sample.getTimestamp() - s.getTimestamp());
|
||||
if (newDist < curDist) {
|
||||
nearestSample = s;
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("Found {} duplicates for {}, updating nearest sample at {}", nearSamples.size(), sample.getTimestamp(), nearestSample.getTimestamp());
|
||||
|
||||
if (sample.getHeartRate() != 0) {
|
||||
nearestSample.setHeartRate(sample.getHeartRate());
|
||||
}
|
||||
if (sample.getSteps() != 0) {
|
||||
nearestSample.setSteps(sample.getSteps());
|
||||
}
|
||||
if (sample.getRawIntensity() != 0) {
|
||||
nearestSample.setRawIntensity(sample.getRawIntensity());
|
||||
}
|
||||
|
||||
addGBActivitySample(nearestSample);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,141 @@
|
||||
/* Copyright (C) 2023 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.banglejs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants.PREF_BANGLEJS_ACTIVITY_FULL_SYNC_START;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants.PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STATUS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants.PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STOP;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants.PREF_BANGLEJS_ACTIVITY_FULL_SYNC_TRIGGER;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class BangleJSSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
private ProgressDialog activityFullSyncDialog;
|
||||
|
||||
final GBDevice device;
|
||||
|
||||
public BangleJSSettingsCustomizer(final GBDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
|
||||
// Handle full sync status
|
||||
if (preference.getKey().equals(PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STATUS)) {
|
||||
final EditTextPreference fullSyncStatusPreference = (EditTextPreference) preference;
|
||||
final String statusValue = fullSyncStatusPreference.getText();
|
||||
|
||||
if (activityFullSyncDialog != null) {
|
||||
switch (statusValue) {
|
||||
case "start":
|
||||
activityFullSyncDialog.setMessage(handler.getContext().getString(R.string.busy_task_fetch_activity_data));
|
||||
break;
|
||||
case "end":
|
||||
activityFullSyncDialog.dismiss();
|
||||
activityFullSyncDialog = null;
|
||||
break;
|
||||
default:
|
||||
activityFullSyncDialog.setMessage(statusValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs) {
|
||||
final Preference fullSyncPref = handler.findPreference(PREF_BANGLEJS_ACTIVITY_FULL_SYNC_TRIGGER);
|
||||
if (fullSyncPref != null) {
|
||||
fullSyncPref.setOnPreferenceClickListener(preference -> {
|
||||
if (activityFullSyncDialog != null) {
|
||||
// Already syncing
|
||||
return true;
|
||||
}
|
||||
|
||||
final Context context = preference.getContext();
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.pref_activity_full_sync_trigger_title)
|
||||
.setMessage(R.string.pref_activity_full_sync_trigger_warning)
|
||||
.setIcon(R.drawable.ic_refresh)
|
||||
.setPositiveButton(R.string.start, (dialog, whichButton) -> {
|
||||
handler.notifyPreferenceChanged(PREF_BANGLEJS_ACTIVITY_FULL_SYNC_START);
|
||||
|
||||
activityFullSyncDialog = new ProgressDialog(context);
|
||||
activityFullSyncDialog.setCancelable(false);
|
||||
activityFullSyncDialog.setMessage(context.getString(R.string.sony_anc_optimizer_status_starting));
|
||||
activityFullSyncDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
activityFullSyncDialog.setProgress(0);
|
||||
activityFullSyncDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.Cancel), (dialog1, which) -> {
|
||||
dialog1.dismiss();
|
||||
activityFullSyncDialog = null;
|
||||
handler.notifyPreferenceChanged(PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STOP);
|
||||
});
|
||||
|
||||
activityFullSyncDialog.show();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPreferenceKeysWithSummary() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeParcelable(device, 0);
|
||||
}
|
||||
|
||||
public static final Creator<BangleJSSettingsCustomizer> CREATOR = new Creator<BangleJSSettingsCustomizer>() {
|
||||
@Override
|
||||
public BangleJSSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
final GBDevice device = in.readParcelable(BangleJSSettingsCustomizer.class.getClassLoader());
|
||||
return new BangleJSSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BangleJSSettingsCustomizer[] newArray(final int size) {
|
||||
return new BangleJSSettingsCustomizer[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
|
@ -25,6 +25,8 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_INTENTS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_INTERNET_ACCESS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.database.DBHelper.getUser;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants.PREF_BANGLEJS_ACTIVITY_FULL_SYNC_START;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants.PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STATUS;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
@ -32,6 +34,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
@ -72,6 +75,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -89,6 +93,7 @@ import io.wax911.emojify.EmojiManager;
|
||||
import io.wax911.emojify.EmojiUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
@ -96,6 +101,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallContro
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BangleJSActivitySample;
|
||||
@ -105,6 +111,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.LocationProviderType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
@ -117,6 +124,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.EmojiConverter;
|
||||
@ -328,27 +336,6 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
getDevice().setFirmwareVersion2("N/A");
|
||||
lastBatteryPercent = -1;
|
||||
|
||||
/* Here we get the last Activity info saved from Bangle.js, and then send
|
||||
its timestamp. Bangle.js can then look back at its history and can try and
|
||||
send any missing data. */
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
BangleJSSampleProvider provider = new BangleJSSampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
BangleJSActivitySample sample = provider.getLatestActivitySample();
|
||||
if (sample!=null) {
|
||||
LOG.info("Send 'actlast' with last activity's timestamp: "+sample.getTimestamp());
|
||||
try {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "actlast");
|
||||
o.put("time", sample.getTimestamp());
|
||||
uartTxJSON("actlast", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Error getting last activity: " + ex.getLocalizedMessage());
|
||||
}
|
||||
|
||||
LOG.info("Initialization Done");
|
||||
|
||||
requestBangleGPSPowerStatus();
|
||||
@ -547,6 +534,9 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
case "notify" :
|
||||
handleNotificationControl(json);
|
||||
break;
|
||||
case "actfetch":
|
||||
handleActivityFetch(json);
|
||||
break;
|
||||
case "act":
|
||||
handleActivity(json);
|
||||
break;
|
||||
@ -626,17 +616,36 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
evaluateGBDeviceEvent(deviceEvtNotificationControl);
|
||||
}
|
||||
|
||||
private void handleActivityFetch(final JSONObject json) throws JSONException {
|
||||
final String state = json.getString("state");
|
||||
if ("start".equals(state)) {
|
||||
GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data),"", true, 0, getContext());
|
||||
getDevice().setBusyTask(getContext().getString(R.string.busy_task_fetch_activity_data));
|
||||
} else if ("end".equals(state)) {
|
||||
saveLastSyncTimestamp(System.currentTimeMillis() - 1000L * 60);
|
||||
getDevice().unsetBusyTask();
|
||||
GB.updateTransferNotification(null, "", false, 100, getContext());
|
||||
} else {
|
||||
LOG.warn("Unknown actfetch state {}", state);
|
||||
}
|
||||
|
||||
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
|
||||
.withPreference(PREF_BANGLEJS_ACTIVITY_FULL_SYNC_STATUS, state);
|
||||
evaluateGBDeviceEvent(event);
|
||||
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "act" packet, used to send activity reports
|
||||
*/
|
||||
private void handleActivity(JSONObject json) throws JSONException {
|
||||
BangleJSActivitySample sample = new BangleJSActivitySample();
|
||||
sample.setTimestamp((int) (System.currentTimeMillis() / 1000L));
|
||||
int hrm = 0;
|
||||
int steps = 0;
|
||||
if (json.has("time")) sample.setTimestamp(json.getInt("time"));
|
||||
if (json.has("hrm")) hrm = json.getInt("hrm");
|
||||
if (json.has("stp")) steps = json.getInt("stp");
|
||||
int timestamp = (int) (json.optLong("ts", System.currentTimeMillis()) / 1000);
|
||||
int hrm = json.optInt("hrm", 0);
|
||||
int steps = json.optInt("stp", 0);
|
||||
int intensity = json.optInt("mov", ActivitySample.NOT_MEASURED);
|
||||
boolean realtime = json.optInt("rt", 0) == 1;
|
||||
int activity = BangleJSSampleProvider.TYPE_ACTIVITY;
|
||||
/*if (json.has("act")) {
|
||||
String actName = "TYPE_" + json.getString("act").toUpperCase();
|
||||
@ -651,21 +660,26 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
LOG.info("JSON activity '"+actName+"' not found");
|
||||
}
|
||||
}*/
|
||||
sample.setTimestamp(timestamp);
|
||||
sample.setRawKind(activity);
|
||||
sample.setHeartRate(hrm);
|
||||
sample.setSteps(steps);
|
||||
sample.setRawIntensity(intensity);
|
||||
if (!realtime) {
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
Long userId = getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
final Long userId = getUser(dbHandler.getDaoSession()).getId();
|
||||
final Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
BangleJSSampleProvider provider = new BangleJSSampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
sample.setDeviceId(deviceId);
|
||||
sample.setUserId(userId);
|
||||
provider.addGBActivitySample(sample);
|
||||
} catch (Exception ex) {
|
||||
provider.upsertSample(sample);
|
||||
} catch (final Exception ex) {
|
||||
LOG.warn("Error saving activity: " + ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// push realtime data
|
||||
if (realtimeHRM || realtimeStep) {
|
||||
if (realtime && (realtimeHRM || realtimeStep)) {
|
||||
Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
|
||||
.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample);
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
@ -942,6 +956,17 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(final String config) {
|
||||
switch (config) {
|
||||
case PREF_BANGLEJS_ACTIVITY_FULL_SYNC_START:
|
||||
fetchActivityData(0);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.warn("Unknown config changed: {}", config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic) {
|
||||
@ -1306,7 +1331,11 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onFetchRecordedData(int dataTypes) {
|
||||
if (dataTypes == RecordedDataTypes.TYPE_DEBUGLOGS) {
|
||||
if ((dataTypes & RecordedDataTypes.TYPE_ACTIVITY) != 0) {
|
||||
fetchActivityData(getLastSuccessfulSyncTime());
|
||||
}
|
||||
|
||||
if ((dataTypes & RecordedDataTypes.TYPE_DEBUGLOGS) != 0) {
|
||||
File dir;
|
||||
try {
|
||||
dir = FileUtils.getExternalFilesDir();
|
||||
@ -1329,6 +1358,37 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
protected void fetchActivityData(final long timestampMillis) {
|
||||
try {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "actfetch");
|
||||
o.put("ts", timestampMillis);
|
||||
uartTxJSON("fetch activity data", o);
|
||||
} catch (final JSONException e) {
|
||||
LOG.warn("Failed to fetch activity data", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getLastSyncTimeKey() {
|
||||
return "lastSyncTimeMillis";
|
||||
}
|
||||
|
||||
protected void saveLastSyncTimestamp(final long timestamp) {
|
||||
final SharedPreferences.Editor editor = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).edit();
|
||||
editor.putLong(getLastSyncTimeKey(), timestamp);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
protected long getLastSuccessfulSyncTime() {
|
||||
long timeStampMillis = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).getLong(getLastSyncTimeKey(), 0);
|
||||
if (timeStampMillis != 0) {
|
||||
return timeStampMillis;
|
||||
}
|
||||
final GregorianCalendar calendar = BLETypeConversions.createCalendar();
|
||||
calendar.add(Calendar.DAY_OF_MONTH, -1);
|
||||
return calendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
if (enable == realtimeHRM) return;
|
||||
|
@ -2222,4 +2222,7 @@
|
||||
<string name="withings_bt_calibration_next">Next</string>
|
||||
<string name="drag_handle">drag handle</string>
|
||||
<string name="find_my_phone_found_it">FOUND IT</string>
|
||||
<string name="pref_activity_full_sync_trigger_summary">Trigger a full sync of all activity data</string>
|
||||
<string name="pref_activity_full_sync_trigger_title">Full sync</string>
|
||||
<string name="pref_activity_full_sync_trigger_warning">This will trigger a full sync of all activity data from the device. It may take a few minutes to complete.</string>
|
||||
</resources>
|
||||
|
23
app/src/main/res/xml/devicesettings_banglejs_activity.xml
Normal file
23
app/src/main/res/xml/devicesettings_banglejs_activity.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_header_banglejs_activity"
|
||||
android:title="@string/Activity">
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/ic_refresh"
|
||||
android:key="pref_banglejs_activity_full_sync_trigger"
|
||||
android:summary="@string/pref_activity_full_sync_trigger_summary"
|
||||
android:title="@string/pref_activity_full_sync_trigger_title" />
|
||||
|
||||
<!-- dummy preference, just to trigger customizer notifications -->
|
||||
<EditTextPreference
|
||||
android:defaultValue="NOT_RUNNING"
|
||||
android:enabled="false"
|
||||
android:key="pref_banglejs_activity_full_sync_status"
|
||||
android:shouldDisableView="false"
|
||||
android:title="pref_banglejs_activity_full_sync_status"
|
||||
app:isPreferenceVisible="false" />
|
||||
</PreferenceCategory>
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user