Garmin / Xiaomi-protobuf: Move activity reprocessing to device settings

This commit is contained in:
José Rebelo
2025-12-25 14:19:36 +00:00
parent f60684e87a
commit c453e3a346
18 changed files with 267 additions and 235 deletions
@@ -269,6 +269,7 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
final List<Integer> developer = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DEVELOPER);
developer.add(R.xml.devicesettings_import_activity_files);
developer.add(R.xml.devicesettings_reprocess_activity_files);
developer.add(R.xml.devicesettings_keep_activity_data_on_device);
developer.add(R.xml.devicesettings_fetch_unknown_files);
developer.add(R.xml.devicesettings_install_unsupported_files);
@@ -30,15 +30,16 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps.GarminAgpsStatus;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitAsyncProcessor;
@@ -46,6 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.notifications.GBProgressNotification;
public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
private static final Logger LOG = LoggerFactory.getLogger(GarminSettingsCustomizer.class);
@@ -68,6 +70,14 @@ public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomize
});
}
final Preference reprocessActivityPref = handler.findPreference("reprocess_activity_files");
if (reprocessActivityPref != null) {
reprocessActivityPref.setOnPreferenceClickListener(preference -> {
parseAllFitFilesFromStorage(handler.getContext(), handler.getDevice());
return true;
});
}
final Preference prefImportActivityFiles = handler.findPreference("import_activity_files");
if (prefImportActivityFiles != null) {
final ActivityResultLauncher<String[]> activityFileChooser = handler.registerForActivityResult(
@@ -135,7 +145,7 @@ public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomize
final String currentFolder = prefs.getString(GarminPreferences.PREF_GARMIN_AGPS_FOLDER, "");
final Preference prefFolder = handler.findPreference(GarminPreferences.PREF_GARMIN_AGPS_FOLDER);
final Preference prefFolder = Objects.requireNonNull(handler.findPreference(GarminPreferences.PREF_GARMIN_AGPS_FOLDER));
final ActivityResultLauncher<Uri> agpsFolderChooser = handler.registerForActivityResult(
new ActivityResultContracts.OpenDocumentTree(),
localUri -> {
@@ -251,9 +261,7 @@ public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomize
builder.setTitle(R.string.garmin_agps_local_file);
final AtomicInteger selectedIdx = new AtomicInteger(0);
builder.setSingleChoiceItems(files, checkedItem, (dialog, which) -> {
selectedIdx.set(which);
});
builder.setSingleChoiceItems(files, checkedItem, (dialog, which) -> selectedIdx.set(which));
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
final String selectedFilename = selectedIdx.get() > 0 ? files[selectedIdx.get()] : null;
prefs.getPreferences().edit()
@@ -268,6 +276,9 @@ public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomize
private void updateAgpsStatus(final DeviceSpecificSettingsHandler handler, final Prefs prefs, final String url) {
final Preference prefStatus = handler.findPreference(GarminPreferences.agpsStatus(url));
if (prefStatus == null) {
return;
}
final String filename = prefs.getString(GarminPreferences.agpsFilename(url), "");
if (filename.isEmpty()) {
@@ -303,7 +314,7 @@ public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomize
return Collections.emptySet();
}
public static final Creator<GarminSettingsCustomizer> CREATOR = new Creator<GarminSettingsCustomizer>() {
public static final Creator<GarminSettingsCustomizer> CREATOR = new Creator<>() {
@Override
public GarminSettingsCustomizer createFromParcel(final Parcel in) {
return new GarminSettingsCustomizer();
@@ -323,4 +334,70 @@ public class GarminSettingsCustomizer implements DeviceSpecificSettingsCustomize
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
}
private static final AtomicBoolean PARSING_FROM_STORAGE = new AtomicBoolean(false);
private static void parseAllFitFilesFromStorage(final Context context, final GBDevice device) {
if (!PARSING_FROM_STORAGE.compareAndSet(false, true)) {
GB.toast(context, "Already parsing!", Toast.LENGTH_LONG, GB.ERROR);
return;
}
LOG.info("Parsing all fit files from storage");
final List<File> fitFiles;
try {
final File exportDir = device.getDeviceCoordinator().getWritableExportDirectory(device, true);
if (!exportDir.exists() || !exportDir.isDirectory()) {
LOG.error("export directory {} not found", exportDir);
GB.toast(context, "export directory " + exportDir + " not found", Toast.LENGTH_LONG, GB.ERROR);
PARSING_FROM_STORAGE.set(false);
return;
}
fitFiles = FileUtils.listRecursive(exportDir, (dir, name) -> name.endsWith(".fit"));
if (fitFiles.isEmpty()) {
LOG.error("No fit files found in {}", exportDir);
GB.toast(context, "No fit files found in " + exportDir, Toast.LENGTH_LONG, GB.ERROR);
PARSING_FROM_STORAGE.set(false);
return;
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
GB.toast(context, "Failed to parse from storage", Toast.LENGTH_LONG, GB.ERROR, e);
PARSING_FROM_STORAGE.set(false);
return;
}
LOG.debug("Got {} fit files to parse", fitFiles.size());
GB.toast(context, "Check notification for progress", Toast.LENGTH_LONG, GB.INFO);
final GBProgressNotification transferNotification = new GBProgressNotification(context, GB.NOTIFICATION_CHANNEL_ID_TRANSFER);
transferNotification.start(R.string.busy_task_processing_files, 0, fitFiles.size());
//try (DBHandler handler = GBApplication.acquireDB()) {
// final DaoSession session = handler.getDaoSession();
// final Device device = DBHelper.getDevice(gbDevice, session);
// //getCoordinator().deleteAllActivityData(device, session);
//} catch (final Exception e) {
// GB.toast(context, "Error deleting activity data", Toast.LENGTH_LONG, GB.ERROR, e);
//}
final FitAsyncProcessor fitAsyncProcessor = new FitAsyncProcessor(context, device);
fitAsyncProcessor.process(fitFiles, new FitAsyncProcessor.Callback() {
@Override
public void onProgress(final int i) {
transferNotification.setTotalProgress(i);
}
@Override
public void onFinish() {
PARSING_FROM_STORAGE.set(false);
transferNotification.finish();
GB.signalActivityDataFinish(device);
}
});
}
}
@@ -535,6 +535,7 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator {
//
deviceSpecificSettings.addRootScreen(
DeviceSpecificSettingsScreen.DEVELOPER,
R.xml.devicesettings_reprocess_activity_files,
R.xml.devicesettings_keep_activity_data_on_device
);
@@ -19,27 +19,55 @@ package nodomain.freeyourgadget.gadgetbridge.devices.xiaomi;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsUtils.hidePrefIfNoneVisible;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsUtils.populateOrHideListPreference;
import android.content.Context;
import android.os.Handler;
import android.os.Parcel;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class XiaomiSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
private static final Logger LOG = LoggerFactory.getLogger(XiaomiSettingsCustomizer.class);
@Override
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
}
@Override
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs, final String rootKey) {
final Preference reprocessActivityPref = handler.findPreference("reprocess_activity_files");
if (reprocessActivityPref != null) {
reprocessActivityPref.setOnPreferenceClickListener(preference -> {
parseAllActivityFilesFromStorage(handler.getContext(), handler.getDevice());
return true;
});
}
final Preference activityMonitoringPref = handler.findPreference(DeviceSettingsPreferenceConst.PREF_HEARTRATE_ACTIVITY_MONITORING);
if (activityMonitoringPref != null) {
activityMonitoringPref.setVisible(false);
@@ -69,7 +97,7 @@ public class XiaomiSettingsCustomizer implements DeviceSpecificSettingsCustomize
return Collections.emptySet();
}
public static final Creator<XiaomiSettingsCustomizer> CREATOR = new Creator<XiaomiSettingsCustomizer>() {
public static final Creator<XiaomiSettingsCustomizer> CREATOR = new Creator<>() {
@Override
public XiaomiSettingsCustomizer createFromParcel(final Parcel in) {
return new XiaomiSettingsCustomizer();
@@ -87,6 +115,107 @@ public class XiaomiSettingsCustomizer implements DeviceSpecificSettingsCustomize
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
}
private static final AtomicBoolean PARSING_FROM_STORAGE = new AtomicBoolean(false);
private static void parseAllActivityFilesFromStorage(final Context context, final GBDevice device) {
if (!PARSING_FROM_STORAGE.compareAndSet(false, true)) {
GB.toast(context, "Already parsing!", Toast.LENGTH_LONG, GB.ERROR);
return;
}
LOG.info("Parsing all activity files from storage");
final List<File> activityFiles;
try {
final File externalFilesDir = device.getDeviceCoordinator().getWritableExportDirectory(device, true);
final File exportDir = new File(externalFilesDir, "rawFetchOperations");
if (!exportDir.exists() || !exportDir.isDirectory()) {
LOG.error("export directory {} not found", exportDir);
GB.toast(context, "export directory " + exportDir + " not found", Toast.LENGTH_LONG, GB.ERROR);
return;
}
activityFiles = FileUtils.listRecursive(exportDir, (dir, name) -> name.endsWith(".bin"));
if (activityFiles.isEmpty()) {
LOG.error("No activity files found in {}", exportDir);
GB.toast(context, "No activity files found in " + exportDir, Toast.LENGTH_LONG, GB.ERROR);
return;
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
GB.toast(context, "Failed to parse from storage", Toast.LENGTH_LONG, GB.ERROR, e);
return;
}
LOG.debug("Will parse {} files", activityFiles.size());
GB.toast(context, "Check notification for progress", Toast.LENGTH_LONG, GB.INFO);
GB.updateTransferNotification("Parsing activity files", "...", true, 0, context);
final long[] lastNotificationUpdateTs = new long[]{System.currentTimeMillis()};
final Handler handler = new Handler(context.getMainLooper());
new Thread(() -> {
try {
int[] i = new int[]{0};
for (final File activityFile : activityFiles) {
i[0]++;
LOG.debug("Parsing {}", activityFile);
final long now = System.currentTimeMillis();
if (now - lastNotificationUpdateTs[0] > 1500L) {
lastNotificationUpdateTs[0] = now;
handler.post(() -> {
GB.updateTransferNotification(
"Parsing activity files", "File " + i[0] + " of " + activityFiles.size(),
true,
(i[0] * 100) / activityFiles.size(), context
);
});
}
// The logic below just replicates XiaomiActivityFileFetcher
final byte[] data;
try (InputStream in = new FileInputStream(activityFile)) {
data = FileUtils.readAll(in, 999999);
} catch (final IOException ioe) {
LOG.error("Failed to read {}", activityFile, ioe);
continue;
}
final byte[] fileIdBytes = Arrays.copyOfRange(data, 0, 7);
final XiaomiActivityFileId fileId = XiaomiActivityFileId.from(fileIdBytes);
final XiaomiActivityParser activityParser = XiaomiActivityParser.create(fileId);
if (activityParser == null) {
LOG.warn("Failed to find parser for {}", fileId);
continue;
}
try {
if (activityParser.parse(context, device, fileId, data)) {
LOG.info("Successfully parsed {}", fileId);
} else {
LOG.warn("Failed to parse {}", fileId);
}
} catch (final Exception ex) {
LOG.error("Exception while parsing {}", fileId, ex);
}
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
}
handler.post(() -> {
PARSING_FROM_STORAGE.set(false);
GB.updateTransferNotification("", "", false, 100, context);
GB.signalActivityDataFinish(device);
});
}, "XiaomiReprocessThread").start();
}
}
@@ -1382,71 +1382,6 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC
@Override
public void onTestNewFunction() {
parseAllFitFilesFromStorage();
}
boolean parsingFitFilesFromStorage = false;
private void parseAllFitFilesFromStorage() {
if (parsingFitFilesFromStorage) {
GB.toast(getContext(), "Already parsing!", Toast.LENGTH_LONG, GB.ERROR);
return;
}
parsingFitFilesFromStorage = true;
LOG.info("Parsing all fit files from storage");
final List<File> fitFiles;
try {
final File exportDir = getWritableExportDirectory();
if (!exportDir.exists() || !exportDir.isDirectory()) {
LOG.error("export directory {} not found", exportDir);
GB.toast(getContext(), "export directory " + exportDir + " not found", Toast.LENGTH_LONG, GB.ERROR);
return;
}
fitFiles = FileUtils.listRecursive(exportDir, (dir, name) -> name.endsWith(".fit"));
if (fitFiles.isEmpty()) {
LOG.error("No fit files found in {}", exportDir);
GB.toast(getContext(), "No fit files found in " + exportDir, Toast.LENGTH_LONG, GB.ERROR);
return;
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
GB.toast(getContext(), "Failed to parse from storage", Toast.LENGTH_LONG, GB.ERROR, e);
return;
}
LOG.debug("Got {} fit files to parse", fitFiles.size());
GB.toast(getContext(), "Check notification for progress", Toast.LENGTH_LONG, GB.INFO);
transferNotification.start(R.string.busy_task_processing_files, 0, fitFiles.size());
//try (DBHandler handler = GBApplication.acquireDB()) {
// final DaoSession session = handler.getDaoSession();
// final Device device = DBHelper.getDevice(gbDevice, session);
// //getCoordinator().deleteAllActivityData(device, session);
//} catch (final Exception e) {
// GB.toast(getContext(), "Error deleting activity data", Toast.LENGTH_LONG, GB.ERROR, e);
//}
final FitAsyncProcessor fitAsyncProcessor = new FitAsyncProcessor(getContext(), getDevice());
fitAsyncProcessor.process(fitFiles, new FitAsyncProcessor.Callback() {
@Override
public void onProgress(final int i) {
transferNotification.setTotalProgress(i);
}
@Override
public void onFinish() {
parsingFitFilesFromStorage = false;
transferNotification.finish();
GB.signalActivityDataFinish(getDevice());
}
});
}
}
@@ -22,8 +22,6 @@ import android.content.Context;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -31,17 +29,11 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
@@ -61,8 +53,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
import nodomain.freeyourgadget.gadgetbridge.model.WorldClock;
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.AbstractXiaomiService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.XiaomiCalendarService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.XiaomiDataUploadService;
@@ -74,13 +64,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.Xiao
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.XiaomiSystemService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.XiaomiWatchfaceService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.XiaomiWeatherService;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class XiaomiSupport extends AbstractDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(XiaomiSupport.class);
private static final AtomicLong THREAD_COUNTER = new AtomicLong(0L);
private final XiaomiAuthService authService = new XiaomiAuthService(this);
private final XiaomiMusicService musicService = new XiaomiMusicService(this);
@@ -248,7 +236,6 @@ public class XiaomiSupport extends AbstractDeviceSupport {
@Override
public void onTestNewFunction() {
//sendCommand("test new function", 2, 29);
parseAllActivityFilesFromStorage();
}
@Override
@@ -451,109 +438,6 @@ public class XiaomiSupport extends AbstractDeviceSupport {
return StringUtils.replaceEach(inputString, EMOJI_SOURCE, EMOJI_TARGET);
}
boolean parsingActivityFilesFromStorage = false;
private void parseAllActivityFilesFromStorage() {
if (parsingActivityFilesFromStorage) {
GB.toast(getContext(), "Already parsing!", Toast.LENGTH_LONG, GB.ERROR);
return;
}
parsingActivityFilesFromStorage = true;
LOG.info("Parsing all activity files from storage");
final List<File> activityFiles;
try {
final File externalFilesDir = getCoordinator().getWritableExportDirectory(getDevice(), true);
final File exportDir = new File(externalFilesDir, "rawFetchOperations");
if (!exportDir.exists() || !exportDir.isDirectory()) {
LOG.error("export directory {} not found", exportDir);
GB.toast(getContext(), "export directory " + exportDir + " not found", Toast.LENGTH_LONG, GB.ERROR);
return;
}
activityFiles = FileUtils.listRecursive(exportDir, (dir, name) -> name.endsWith(".bin"));
if (activityFiles.isEmpty()) {
LOG.error("No activity files found in {}", exportDir);
GB.toast(getContext(), "No activity files found in " + exportDir, Toast.LENGTH_LONG, GB.ERROR);
return;
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
GB.toast(getContext(), "Failed to parse from storage", Toast.LENGTH_LONG, GB.ERROR, e);
return;
}
LOG.debug("Will parse {} files", activityFiles.size());
GB.toast(getContext(), "Check notification for progress", Toast.LENGTH_LONG, GB.INFO);
GB.updateTransferNotification("Parsing activity files", "...", true, 0, getContext());
final long[] lastNotificationUpdateTs = new long[]{System.currentTimeMillis()};
final Handler handler = new Handler(getContext().getMainLooper());
new Thread(() -> {
try {
int[] i = new int[]{0};
for (final File activityFile : activityFiles) {
i[0]++;
LOG.debug("Parsing {}", activityFile);
final long now = System.currentTimeMillis();
if (now - lastNotificationUpdateTs[0] > 1500L) {
lastNotificationUpdateTs[0] = now;
handler.post(() -> {
GB.updateTransferNotification(
"Parsing activity files", "File " + i[0] + " of " + activityFiles.size(),
true,
(i[0] * 100) / activityFiles.size(), getContext()
);
});
}
// The logic below just replicates XiaomiActivityFileFetcher
final byte[] data;
try (InputStream in = new FileInputStream(activityFile)) {
data = FileUtils.readAll(in, 999999);
} catch (final IOException ioe) {
LOG.error("Failed to read {}", activityFile, ioe);
continue;
}
final byte[] fileIdBytes = Arrays.copyOfRange(data, 0, 7);
final XiaomiActivityFileId fileId = XiaomiActivityFileId.from(fileIdBytes);
final XiaomiActivityParser activityParser = XiaomiActivityParser.create(fileId);
if (activityParser == null) {
LOG.warn("Failed to find parser for {}", fileId);
continue;
}
try {
if (activityParser.parse(this, fileId, data)) {
LOG.info("Successfully parsed {}", fileId);
} else {
LOG.warn("Failed to parse {}", fileId);
}
} catch (final Exception ex) {
LOG.error("Exception while parsing {}", fileId, ex);
}
}
} catch (final Exception e) {
LOG.error("Failed to parse from storage", e);
}
handler.post(() -> {
parsingActivityFilesFromStorage = false;
GB.updateTransferNotification("", "", false, 100, getContext());
GB.signalActivityDataFinish(getDevice());
});
}, "XiaomiSupport_" + THREAD_COUNTER.getAndIncrement()).start();
}
public void setFeatureSupported(final String featureKey, final boolean supported) {
LOG.debug("Setting feature {} -> {}", featureKey, supported ? "supported" : "not supported");
evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(featureKey, supported));
@@ -144,7 +144,7 @@ public class XiaomiActivityFileFetcher {
}
try {
if (activityParser.parse(mHealthService.getSupport(), fileId, data)) {
if (activityParser.parse(mHealthService.getSupport().getContext(), mHealthService.getSupport().getDevice(), fileId, data)) {
LOG.info("Successfully parsed {}", fileId);
} else {
LOG.warn("Failed to parse {}", fileId);
@@ -202,9 +202,8 @@ public class XiaomiActivityFileFetcher {
}
@Nullable
public static File getRawFile(final XiaomiSupport support, final XiaomiActivityFileId fileId) {
public static File getRawFile(final GBDevice device, final XiaomiActivityFileId fileId) {
try {
final GBDevice device = support.getDevice();
final File exportDirectory = device.getDeviceCoordinator().getWritableExportDirectory(device, true);
final File targetDir = new File(exportDirectory, "rawFetchOperations");
final File outputFile = fileId.getOutputFile(targetDir);
@@ -220,7 +219,7 @@ public class XiaomiActivityFileFetcher {
protected void dumpBytesToExternalStorage(final XiaomiActivityFileId fileId, final byte[] bytes) {
try {
final File outputFile = getRawFile(mHealthService.getSupport(), fileId);
final File outputFile = getRawFile(mHealthService.getSupport().getDevice(), fileId);
final File parentFile = outputFile.getParentFile();
if (parentFile != null) {
//noinspection ResultOfMethodCallIgnored
@@ -16,6 +16,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity;
import android.content.Context;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
@@ -29,8 +31,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.ManualSamplesParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.DailyDetailsParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.DailySummaryParser;
@@ -42,7 +44,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl
public abstract class XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(XiaomiActivityParser.class);
public abstract boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes);
public abstract boolean parse(final Context context, GBDevice device, final XiaomiActivityFileId fileId, final byte[] bytes);
protected BaseActivitySummary findOrCreateBaseActivitySummary(final DaoSession session,
final Device device,
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
@@ -38,7 +39,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiActivitySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -47,7 +47,7 @@ public class DailyDetailsParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(DailyDetailsParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
final int version = fileId.getVersion();
final int headerSize;
switch (version) {
@@ -182,7 +182,6 @@ public class DailyDetailsParser extends XiaomiActivityParser {
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
final DeviceCoordinator coordinator = gbDevice.getDeviceCoordinator();
final SampleProvider<XiaomiActivitySample> sampleProvider = (SampleProvider<XiaomiActivitySample>) coordinator.getSampleProvider(gbDevice, session);
final Device device = DBHelper.getDevice(gbDevice, session);
@@ -197,7 +196,7 @@ public class DailyDetailsParser extends XiaomiActivityParser {
return true;
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving activity samples", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving activity samples", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving activity samples", e);
return false;
}
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
@@ -31,7 +32,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiDailySummarySam
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiDailySummarySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -40,7 +40,7 @@ public class DailySummaryParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(DailySummaryParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice device, final XiaomiActivityFileId fileId, final byte[] bytes) {
final int version = fileId.getVersion();
final int headerSize;
switch (version) {
@@ -113,7 +113,6 @@ public class DailySummaryParser extends XiaomiActivityParser {
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice device = support.getDevice();
sample.setDevice(DBHelper.getDevice(device, session));
sample.setUser(DBHelper.getUser(session));
@@ -121,7 +120,7 @@ public class DailySummaryParser extends XiaomiActivityParser {
final XiaomiDailySummarySampleProvider sampleProvider = new XiaomiDailySummarySampleProvider(device, session);
sampleProvider.addSample(sample);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving daily summary", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving daily summary", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving daily summary", e);
return false;
}
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
@@ -35,7 +36,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiManualSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -44,7 +44,7 @@ public class ManualSamplesParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(ManualSamplesParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
if (fileId.getVersion() != 2) {
LOG.warn("Unknown manual samples version {}", fileId.getVersion());
return false;
@@ -110,7 +110,6 @@ public class ManualSamplesParser extends XiaomiActivityParser {
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
@@ -122,7 +121,7 @@ public class ManualSamplesParser extends XiaomiActivityParser {
final XiaomiManualSampleProvider sampleProvider = new XiaomiManualSampleProvider(gbDevice, session);
sampleProvider.addSamples(samples);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving manual samples", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving manual samples", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving manual samples", e);
return false;
}
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
@@ -40,7 +41,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepStageSample;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -49,7 +49,7 @@ public class SleepDetailsParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(SleepDetailsParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
// Seems to come both as DetailType.DETAILS (version 2) and DetailType.SUMMARY (version 4, 5)
final int version = fileId.getVersion();
final int headerSize;
@@ -314,7 +314,6 @@ public class SleepDetailsParser extends XiaomiActivityParser {
// save all the samples that we got
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
final XiaomiSleepTimeSampleProvider sampleProvider = new XiaomiSleepTimeSampleProvider(gbDevice, session);
@@ -337,7 +336,7 @@ public class SleepDetailsParser extends XiaomiActivityParser {
sampleProvider.addSample(summary);
}
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving sleep sample", e);
persistSuccess = false;
}
@@ -348,7 +347,6 @@ public class SleepDetailsParser extends XiaomiActivityParser {
// Save the sleep stage samples
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
@@ -361,7 +359,7 @@ public class SleepDetailsParser extends XiaomiActivityParser {
sampleProvider.addSamples(stages);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving sleep stage samples", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving sleep stage samples", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving sleep stage samples", e);
persistSuccess = false;
}
@@ -370,7 +368,6 @@ public class SleepDetailsParser extends XiaomiActivityParser {
// Save the heart pulse samples
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
@@ -383,7 +380,7 @@ public class SleepDetailsParser extends XiaomiActivityParser {
sampleProvider.addSamples(heartPulseSamples);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving heart pulse samples", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving heart pulse samples", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving heart pulse samples", e);
persistSuccess = false;
}
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
@@ -37,7 +38,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepStageSample;
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -46,7 +46,7 @@ public class SleepStagesParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(SleepStagesParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
if (fileId.getVersion() != 2) {
LOG.warn("Unknown sleep stages version {}", fileId.getVersion());
return false;
@@ -124,7 +124,6 @@ public class SleepStagesParser extends XiaomiActivityParser {
// Save the sleep time sample
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
sample.setDevice(DBHelper.getDevice(gbDevice, session));
sample.setUser(DBHelper.getUser(session));
@@ -145,7 +144,7 @@ public class SleepStagesParser extends XiaomiActivityParser {
sampleProvider.addSample(sample);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving sleep sample", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving sleep sample", e);
return false;
}
@@ -153,7 +152,6 @@ public class SleepStagesParser extends XiaomiActivityParser {
// Save the sleep stage samples
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final GBDevice gbDevice = support.getDevice();
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
@@ -166,7 +164,7 @@ public class SleepStagesParser extends XiaomiActivityParser {
sampleProvider.addSamples(stages);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving sleep stage samples", Toast.LENGTH_LONG, GB.ERROR);
GB.toast(context, "Error saving sleep stage samples", Toast.LENGTH_LONG, GB.ERROR);
LOG.error("Error saving sleep stage samples", e);
return false;
}
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl;
import android.content.Context;
import android.widget.Toast;
import org.slf4j.Logger;
@@ -35,11 +36,11 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.export.ActivityTrackExporter;
import nodomain.freeyourgadget.gadgetbridge.export.GPXExporter;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileFetcher;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
@@ -51,7 +52,7 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
private static final Logger LOG = LoggerFactory.getLogger(WorkoutGpsParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
final int version = fileId.getVersion();
final int headerSize;
final int sampleSize;
@@ -119,7 +120,7 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
final DaoSession session = dbHandler.getDaoSession();
final Device device = DBHelper.getDevice(support.getDevice(), session);
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
// Find the matching summary
@@ -128,10 +129,10 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
// Set the info on the activity track
activityTrack.setUser(user);
activityTrack.setDevice(device);
activityTrack.setName(ActivityKind.fromCode(summary.getActivityKind()).getLabel(support.getContext()));
activityTrack.setName(ActivityKind.fromCode(summary.getActivityKind()).getLabel(context));
// The file was already persisted by XiaomiActivityFileFetcher - just use the existing file
final File rawBytesFile = XiaomiActivityFileFetcher.getRawFile(support, fileId);
final File rawBytesFile = XiaomiActivityFileFetcher.getRawFile(gbDevice, fileId);
// Save the gpx file
final GPXExporter exporter = new GPXExporter();
@@ -144,7 +145,7 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
exporter.performExport(activityTrack, gpxTargetFile);
} catch (final ActivityTrackExporter.GPXTrackEmptyException ex) {
exportGpxSuccess = false;
GB.toast(support.getContext(), "This activity does not contain GPX tracks.", Toast.LENGTH_LONG, GB.ERROR, ex);
GB.toast(context, "This activity does not contain GPX tracks.", Toast.LENGTH_LONG, GB.ERROR, ex);
}
if (exportGpxSuccess) {
@@ -155,7 +156,7 @@ public class WorkoutGpsParser extends XiaomiActivityParser {
}
session.getBaseActivitySummaryDao().insertOrReplace(summary);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving workout gps", Toast.LENGTH_LONG, GB.ERROR, e);
GB.toast(context, "Error saving workout gps", Toast.LENGTH_LONG, GB.ERROR, e);
return false;
}
@@ -77,6 +77,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.WORKOUT_LOAD;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.impl.XiaomiSimpleActivityParser.XIAOMI_WORKOUT_TYPE;
import android.content.Context;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -94,10 +95,10 @@ import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
@@ -107,7 +108,7 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
private static final Logger LOG = LoggerFactory.getLogger(WorkoutSummaryParser.class);
@Override
public boolean parse(final XiaomiSupport support, final XiaomiActivityFileId fileId, final byte[] bytes) {
public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
BaseActivitySummary summary = new BaseActivitySummary();
summary.setStartTime(fileId.getTimestamp()); // due to a bug this has to be set
@@ -119,7 +120,7 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
summary = parseBinaryData(summary, true);
} catch (final Exception e) {
LOG.error("Failed to parse workout summary", e);
GB.toast(support.getContext(), "Failed to parse workout summary", Toast.LENGTH_LONG, GB.ERROR, e);
GB.toast(context, "Failed to parse workout summary", Toast.LENGTH_LONG, GB.ERROR, e);
return false;
}
@@ -133,7 +134,7 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
try (DBHandler dbHandler = GBApplication.acquireDB()) {
final DaoSession session = dbHandler.getDaoSession();
final Device device = DBHelper.getDevice(support.getDevice(), session);
final Device device = DBHelper.getDevice(gbDevice, session);
final User user = DBHelper.getUser(session);
final BaseActivitySummary existingSummary = findOrCreateBaseActivitySummary(session, device, user, fileId);
@@ -144,7 +145,7 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
session.getBaseActivitySummaryDao().insertOrReplace(existingSummary);
} catch (final Exception e) {
GB.toast(support.getContext(), "Error saving activity summary", Toast.LENGTH_LONG, GB.ERROR, e);
GB.toast(context, "Error saving activity summary", Toast.LENGTH_LONG, GB.ERROR, e);
return false;
}
+2
View File
@@ -3971,6 +3971,8 @@
<string name="battery_percentage_str">%1$s%%</string>
<string name="pref_import_activity_files_title">Import activity files</string>
<string name="pref_import_activity_files_summary">Manually import activity files from the phone storage</string>
<string name="pref_reprocess_activity_files_title">Reprocess activity files</string>
<string name="pref_reprocess_activity_files_summary">Reprocess all activity files from the phone storage</string>
<string name="pref_fetch_unknown_files_title">Fetch unknown files</string>
<string name="pref_fetch_unknown_files_summary">Fetch unknown activity files from the watch. They will not be processed, but will be saved in the phone.</string>
<string name="pref_install_unsupported_files_title">Install unsupported files</string>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:icon="@drawable/ic_refresh"
android:key="reprocess_activity_files"
android:summary="@string/pref_reprocess_activity_files_summary"
android:title="@string/pref_reprocess_activity_files_title" />
</androidx.preference.PreferenceScreen>
@@ -19,6 +19,6 @@ public class XiaomiActivityParserTest extends TestBase {
final XiaomiActivityFileId fileId = XiaomiActivityFileId.from(fileIdBytes);
final XiaomiActivityParser parser = XiaomiActivityParser.create(fileId);
parser.parse(null, fileId, data);
parser.parse(null, null, fileId, data);
}
}