/*
* Pedometer - Android App
* Copyright (C) 2009 Levente Bagi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package name.bagi.levente.pedometer;
import java.util.ArrayList;
/**
* Calculates and displays pace (steps / minute), handles input of desired pace,
* notifies user if he/she has to go faster or slower.
* @author Levente Bagi
*/
public class PaceNotifier implements StepListener, SpeakingTimer.Listener {
public interface Listener {
public void paceChanged(int value);
public void passValue();
}
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
int mCounter = 0;
private long mLastStepTime = 0;
private long[] mLastStepDeltas = {-1, -1, -1, -1};
private int mLastStepDeltasIndex = 0;
private long mPace = 0;
PedometerSettings mSettings;
Utils mUtils;
/** Desired pace, adjusted by the user */
int mDesiredPace;
/** Should we speak? */
boolean mShouldTellFasterslower;
/** When did the TTS speak last time */
private long mSpokenAt = 0;
public PaceNotifier(PedometerSettings settings, Utils utils) {
mUtils = utils;
mSettings = settings;
mDesiredPace = mSettings.getDesiredPace();
reloadSettings();
}
public void setPace(int pace) {
mPace = pace;
int avg = (int)(60*1000.0 / mPace);
for (int i = 0; i < mLastStepDeltas.length; i++) {
mLastStepDeltas[i] = avg;
}
notifyListener();
}
public void reloadSettings() {
mShouldTellFasterslower =
mSettings.shouldTellFasterslower()
&& mSettings.getMaintainOption() == PedometerSettings.M_PACE;
notifyListener();
}
public void addListener(Listener l) {
mListeners.add(l);
}
public void setDesiredPace(int desiredPace) {
mDesiredPace = desiredPace;
}
public void onStep() {
long thisStepTime = System.currentTimeMillis();
mCounter ++;
// Calculate pace based on last x steps
if (mLastStepTime > 0) {
long delta = thisStepTime - mLastStepTime;
mLastStepDeltas[mLastStepDeltasIndex] = delta;
mLastStepDeltasIndex = (mLastStepDeltasIndex + 1) % mLastStepDeltas.length;
long sum = 0;
boolean isMeaningfull = true;
for (int i = 0; i < mLastStepDeltas.length; i++) {
if (mLastStepDeltas[i] < 0) {
isMeaningfull = false;
break;
}
sum += mLastStepDeltas[i];
}
if (isMeaningfull && sum > 0) {
long avg = sum / mLastStepDeltas.length;
mPace = 60*1000 / avg;
// TODO: remove duplication. This also exists in SpeedNotifier
if (mShouldTellFasterslower && !mUtils.isSpeakingEnabled()) {
if (thisStepTime - mSpokenAt > 3000 && !mUtils.isSpeakingNow()) {
float little = 0.10f;
float normal = 0.30f;
float much = 0.50f;
boolean spoken = true;
if (mPace < mDesiredPace * (1 - much)) {
mUtils.say("much faster!");
}
else
if (mPace > mDesiredPace * (1 + much)) {
mUtils.say("much slower!");
}
else
if (mPace < mDesiredPace * (1 - normal)) {
mUtils.say("faster!");
}
else
if (mPace > mDesiredPace * (1 + normal)) {
mUtils.say("slower!");
}
else
if (mPace < mDesiredPace * (1 - little)) {
mUtils.say("a little faster!");
}
else
if (mPace > mDesiredPace * (1 + little)) {
mUtils.say("a little slower!");
}
else {
spoken = false;
}
if (spoken) {
mSpokenAt = thisStepTime;
}
}
}
}
else {
mPace = -1;
}
}
mLastStepTime = thisStepTime;
notifyListener();
}
private void notifyListener() {
for (Listener listener : mListeners) {
listener.paceChanged((int)mPace);
}
}
public void passValue() {
// Not used
}
//-----------------------------------------------------
// Speaking
public void speak() {
if (mSettings.shouldTellPace()) {
if (mPace > 0) {
mUtils.say(mPace + " steps per minute");
}
}
}
}