/* Copyright (C) 2016-2017 Carsten Pfeiffer 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.service.devices.miband; import java.util.Timer; import java.util.TimerTask; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; /** * Basic support for aggregating different sources of realtime data that comes in in a mostly * fixed interval. The aggregated data will be stored together. * * start() and stop() may be called multiple times, but the first stop() call will really * stop the timer. * manner. * * Subclasses must implement #doCurrentSample() and should override #resetCurrentValues() * (but call super!). */ public abstract class RealtimeSamplesSupport { private final long delay; private final long period; protected int steps; protected int heartrateBpm; private int lastSteps; // subclasses may add more private Timer realtimeStorageTimer; public RealtimeSamplesSupport(long delay, long period) { this.delay = delay; this.period = period; } public synchronized void start() { if (isRunning()) { return; // already running } realtimeStorageTimer = new Timer("Mi Band Realtime Storage Timer"); realtimeStorageTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { triggerCurrentSample(); } }, delay, period); } public synchronized void stop() { if (realtimeStorageTimer != null) { realtimeStorageTimer.cancel(); realtimeStorageTimer.purge(); realtimeStorageTimer = null; } } public synchronized boolean isRunning() { return realtimeStorageTimer != null; } public synchronized void setSteps(int stepsPerMinute) { this.steps = stepsPerMinute; } /** * Returns the number of steps recorded since the last measurements. If no * steps are available yet, ActivitySample.NOT_MEASURED is returned. * @return */ public synchronized int getSteps() { if (steps == ActivitySample.NOT_MEASURED) { return ActivitySample.NOT_MEASURED; } if (lastSteps == 0) { return ActivitySample.NOT_MEASURED; // wait until we have a delta between two samples } int delta = steps - lastSteps; if (delta < 0) { return 0; } return delta; } public void setHeartrateBpm(int hrBpm) { this.heartrateBpm = hrBpm; } public int getHeartrateBpm() { return heartrateBpm; } public void triggerCurrentSample() { doCurrentSample(); resetCurrentValues(); } protected synchronized void resetCurrentValues() { if (steps >= lastSteps) { lastSteps = steps; } steps = ActivitySample.NOT_MEASURED; heartrateBpm = ActivitySample.NOT_MEASURED; } protected abstract void doCurrentSample(); }