diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/XiaomiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/XiaomiSupport.java index bb200fd6d..ff25570e2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/XiaomiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/XiaomiSupport.java @@ -23,6 +23,8 @@ import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.location.Location; import android.net.Uri; +import android.os.Handler; +import android.widget.Toast; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -39,7 +41,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.BuildConfig; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences; @@ -463,66 +464,111 @@ public class XiaomiSupport extends AbstractDeviceSupport { return StringUtils.replaceEach(inputString, EMOJI_SOURCE, EMOJI_TARGET); } + boolean parsingActivityFilesFromStorage = false; + private void parseAllActivityFilesFromStorage() { - // This function as-is should only be used for debug purposes - if (!BuildConfig.DEBUG) { - LOG.error("This should never be used in release builds"); + if (parsingActivityFilesFromStorage) { + GB.toast(getContext(), "Already parsing!", Toast.LENGTH_LONG, GB.ERROR); return; } + parsingActivityFilesFromStorage = true; + LOG.info("Parsing all activity files from storage"); + final File[] activityFiles; try { final File externalFilesDir = getCoordinator().getWritableExportDirectory(getDevice()); - final File targetDir = new File(externalFilesDir, "rawFetchOperations"); + final File exportDir = new File(externalFilesDir, "rawFetchOperations"); - if (!targetDir.exists()) { - LOG.warn("rawFetchOperations not found"); + 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; } - final File[] activityFiles = targetDir.listFiles((dir, name) -> name.startsWith("xiaomi_")); - + activityFiles = exportDir.listFiles((dir, name) -> name.startsWith("xiaomi_")); if (activityFiles == null) { - LOG.warn("activityFiles is null"); + LOG.error("activityFiles is null for {}", exportDir); + GB.toast(getContext(), "activityFiles is null for " + exportDir, Toast.LENGTH_LONG, GB.ERROR); return; } - - for (final File activityFile : activityFiles) { - LOG.debug("Parsing {}", activityFile); - - // 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); - } + if (activityFiles.length == 0) { + 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; } + + 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.length, + true, + (i[0] * 100) / activityFiles.length, 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()); + }); + }).start(); } public void setFeatureSupported(final String featureKey, final boolean supported) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileFetcher.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileFetcher.java index fdfa7ca46..57cdea2fa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileFetcher.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileFetcher.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.PriorityQueue; import java.util.Queue; -import nodomain.freeyourgadget.gadgetbridge.BuildConfig; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; @@ -38,7 +37,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiPrefere import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.XiaomiHealthService; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; -import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class XiaomiActivityFileFetcher { @@ -119,7 +117,7 @@ public class XiaomiActivityFileFetcher { LOG.warn("Failed to parse {}", fileId); } } catch (final Exception ex) { - LOG.error("Exception while parsing " + fileId, ex); + LOG.error("Exception while parsing {}", fileId, ex); } triggerNextFetch();