/*
* Copyright (C) 2012 - 2013 jonas.oreland@gmail.com
*
* 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 org.runnerup.workout;
import android.annotation.TargetApi;
import android.content.ContentValues;
import android.os.Build;
import org.runnerup.BuildConfig;
import org.runnerup.common.util.Constants.DB;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@TargetApi(Build.VERSION_CODES.FROYO)
public class Step implements TickComponent {
String name = null;
/**
* Intensity
*/
Intensity intensity = Intensity.ACTIVE;
/**
* Duration
*/
Dimension durationType = null;
double durationValue = 0;
/**
* Target
*/
Dimension targetType = null;
Range targetValue = null;
/**
* Autolap (m)
*/
double autolap = 0;
/**
* Triggers
*/
final ArrayList<Trigger> triggers = new ArrayList<Trigger>();
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the durationType
*/
public Dimension getDurationType() {
return durationType;
}
/**
* @param durationType the durationType to set
*/
public void setDurationType(Dimension durationType) {
this.durationType = durationType;
}
/**
* @return the durationValue
*/
public double getDurationValue() {
return durationValue;
}
/**
* @param durationValue the durationValue to set
*/
public void setDurationValue(double durationValue) {
this.durationValue = durationValue;
}
/**
* @return the targetType
*/
public Dimension getTargetType() {
return targetType;
}
/**
* @param targetType the targetType to set
*/
public void setTargetType(Dimension targetType) {
this.targetType = targetType;
}
/**
* @return the targetValue
*/
public Range getTargetValue() {
return targetValue;
}
/**
* @param targetValue the targetValue to set
*/
public void setTargetValue(double targetValue) {
this.targetValue = new Range(targetValue, targetValue);
}
public void setTargetValue(double min, double max) {
this.targetValue = new Range(min, max);
}
public Intensity getIntensity() {
return intensity;
}
public void setIntensity(Intensity intensity) {
this.intensity = intensity;
}
/**
* @return the autolap
*/
public double getAutolap() {
return autolap;
}
/**
* @param val the autolap to set
*/
public void setAutolap(double val) {
this.autolap = val;
}
@Override
public void onInit(Workout s) {
for (Trigger t : triggers) {
t.onInit(s);
}
}
@Override
public void onBind(Workout s, HashMap<String, Object> bindValues) {
for (Trigger t : triggers) {
t.onBind(s, bindValues);
}
}
@Override
public void onEnd(Workout s) {
for (Trigger t : triggers) {
t.onEnd(s);
}
}
public void onRepeat(int current, int count) {
for (Trigger t : triggers) {
t.onRepeat(current, count);
}
}
double stepStartTime = 0;
double stepStartDistance = 0;
double stepStartHeartbeats = 0;
double lapStartTime = 0;
double lapStartDistance = 0;
double lapStartHeartbeats = 0;
@Override
public void onStart(Scope what, Workout s) {
double time = s.getTime(Scope.ACTIVITY);
double dist = s.getDistance(Scope.ACTIVITY);
double beats = s.getHeartbeats(Scope.ACTIVITY);
if (what == Scope.STEP) {
stepStartTime = time;
stepStartDistance = dist;
stepStartHeartbeats = beats;
if (s.isPaused())
s.tracker.pause();
else
s.tracker.resume();
} else if (what == Scope.LAP) {
lapStartTime = time;
lapStartDistance = dist;
lapStartHeartbeats = beats;
ContentValues tmp = new ContentValues();
tmp.put(DB.LAP.INTENSITY, intensity.getValue());
if (durationType != null) {
switch (durationType) {
case TIME:
tmp.put(DB.LAP.PLANNED_TIME, (long) durationValue);
break;
case DISTANCE:
tmp.put(DB.LAP.PLANNED_DISTANCE, (long) durationValue);
break;
case PACE:
case SPEED:
case HR:
case HRZ:
case CAD:
case TEMPERATURE:
case PRESSURE:
break;
}
}
if (targetType != null) {
switch (targetType) {
case PACE:
tmp.put(DB.LAP.PLANNED_PACE, targetValue.maxValue);
break;
case SPEED:
if (targetValue.maxValue != 0) {
tmp.put(DB.LAP.PLANNED_PACE, 1.0d / targetValue.maxValue);
}
break;
case DISTANCE:
case TIME:
case HR:
case HRZ:
case CAD:
case TEMPERATURE:
case PRESSURE:
break;
}
}
s.newLap(tmp);
}
for (Trigger t : triggers) {
t.onStart(what, s);
}
}
@Override
public void onStop(Workout s) {
s.tracker.stop();
for (Trigger t : triggers) {
t.onStop(s);
}
/**
* Save current lap so that it shows in DetailActivity
*/
long distance = Math.round(s.getDistance(Scope.LAP));
long time = Math.round(s.getTime(Scope.LAP));
long hr = Math.round(s.getHeartRate(Scope.LAP));
if (distance > 0 || time > 0) {
ContentValues tmp = new ContentValues();
tmp.put(DB.LAP.DISTANCE, distance);
tmp.put(DB.LAP.TIME, time);
tmp.put(DB.LAP.AVG_HR, Math.round(hr));
s.saveLap(tmp, /** next lap */
false);
}
}
@Override
public void onPause(Workout s) {
s.tracker.pause();
for (Trigger t : triggers) {
t.onPause(s);
}
}
/**
* @return true if finished
*/
public boolean onTick(Workout s) {
if (checkFinished(s)) {
return true;
}
for (Trigger t : triggers) {
t.onTick(s);
}
if (this.autolap > 0 && s.getDistance(Scope.LAP) >= this.autolap) {
s.onNewLap();
}
return false;
}
public boolean onNextStep(Workout w) {
return true; // move to next step
}
private boolean checkFinished(Workout s) {
if (durationType == null)
return false;
return s.get(Scope.STEP, durationType) >= this.durationValue;
}
@Override
public void onResume(Workout s) {
for (Trigger t : triggers) {
t.onResume(s);
}
s.tracker.resume();
}
@Override
public void onComplete(Scope scope, Workout s) {
if (scope == Scope.LAP) {
long distance = Math.round(s.getDistance(scope));
long time = Math.round(s.getTime(scope));
double hr = s.getHeartRate(scope);
if (distance > 0 || time > 0) {
ContentValues tmp = new ContentValues();
tmp.put(DB.LAP.DISTANCE, distance);
tmp.put(DB.LAP.TIME, time);
tmp.put(DB.LAP.AVG_HR, Math.round(hr));
s.saveLap(tmp, /** next lap */
true);
}
}
for (Trigger t : triggers) {
t.onComplete(scope, s);
}
if (scope == Scope.STEP) {
for (Trigger t : triggers) {
t.onEnd(s);
}
}
}
public double getDistance(Workout w, Scope s) {
double d = w.getDistance(Scope.ACTIVITY);
if (s == Scope.STEP) {
return d - stepStartDistance;
} else if (s == Scope.LAP) {
return d - lapStartDistance;
}
if (BuildConfig.DEBUG) { throw new AssertionError(); }
return 0;
}
public double getTime(Workout w, Scope s) {
double t = w.getTime(Scope.ACTIVITY);
if (s == Scope.STEP) {
return t - stepStartTime;
} else if (s == Scope.LAP) {
return t - lapStartTime;
}
if (org.runnerup.BuildConfig.DEBUG) { throw new AssertionError(); }
return 0;
}
public double getSpeed(Workout w, Scope s) {
double t = getTime(w, s);
double d = getDistance(w, s);
if (t != 0) {
return d / t;
}
return 0;
}
public double getHeartbeats(Workout w, Scope s) {
double t = w.getHeartbeats(Scope.ACTIVITY);
if (s == Scope.STEP) {
return t - stepStartHeartbeats;
} else if (s == Scope.LAP) {
return t - lapStartHeartbeats;
}
return 0;
}
public double getDuration(Dimension dimension) {
if (durationType == dimension)
return durationValue;
return 0;
}
public static Step createPauseStep(Dimension dim, double duration) {
Step step = null;
if (dim == null || dim == Dimension.TIME)
step = new PauseStep();
else
step = new Step();
step.intensity = Intensity.RESTING;
step.durationType = dim;
step.durationValue = duration;
return step;
}
public void getSteps(Step parent, int i, List<Workout.StepListEntry> list) {
list.add(new Workout.StepListEntry(list.size(), this, i, parent));
}
public Step getCurrentStep() {
return this;
}
public int getRepeatCount() {
return 0;
}
public void setRepeatCount(int val) { }
public int getCurrentRepeat() {
return 0;
}
public boolean isLastStep() {
return true;
}
public boolean isPauseStep() { return false; }
}