package se.johan.wendler.sql;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.text.format.Time;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import se.johan.wendler.model.AdditionalExercise;
import se.johan.wendler.model.DeloadItem;
import se.johan.wendler.model.ExerciseSet;
import se.johan.wendler.model.MainExercise;
import se.johan.wendler.model.SetGroup;
import se.johan.wendler.model.SetType;
import se.johan.wendler.model.Workout;
import se.johan.wendler.util.Constants;
import se.johan.wendler.util.PreferenceUtil;
import se.johan.wendler.util.StringHelper;
import se.johan.wendler.util.WendlerConstants;
import se.johan.wendler.util.WendlerMath;
import se.johan.wendler.util.WendlerizedLog;
/**
* A class for handling queries against the SQL database
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public class SqlHandler {
public static final String DATABASE_NAME = "WendlerizedDb";
private static final int DATABASE_VERSION = 12;
/**
* Stats table *
*/
private static final String DATABASE_TABLE_WENDLER_STATS = "wendler_stats";
private static final String KEY_ROW_ID = "_id";
private static final String KEY_TRAINING_PERCENTAGE = "working_percentage";
private static final String KEY_1RM = "one_rm";
private static final String KEY_ORDER = "order_in_week";
private static final String KEY_INCREMENT = "increment";
private static final String KEY_WEEK = "week";
private static final String KEY_CYCLE = "cycle";
private static final String KEY_CYCLE_NAME = "cycle_name";
private static final String KEY_NAME = "name";
private static final String KEY_SHOULD_DELOAD = "should_deload";
/**
* Percentage table *
*/
private static final String DATABASE_TABLE_PERCENT = "wendler_percentages";
private static final String KEY_WEEK_ONE = "percent_week_1";
private static final String KEY_WEEK_TWO = "percent_week_2";
private static final String KEY_WEEK_THREE = "percent_week_3";
private static final String KEY_WEEK_FOUR = "percent_week_4";
/**
* Workout table *
*/
private static final String DATABASE_TABLE_WENDLER_WORKOUT = "wendler_workout";
private static final String KEY_WORKOUT_ID = "workout_id";
private static final String KEY_INSERT_TIME = "insert_time";
private static final String KEY_WORKOUT_YEAR = "workout_year";
private static final String KEY_WORKOUT_MONTH = "workout_month";
private static final String KEY_WORKOUT_DAY = "workout_day";
private static final String KEY_WORKOUT_EXERCISE = "exercise";
private static final String KEY_WORKOUT_REPS = "reps";
private static final String KEY_WORKOUT_ONE_RM = "one_rm";
private static final String KEY_WORKOUT_LAST_SET = "last_set_weight";
private static final String KEY_WORKOUT_WEEK = "week";
private static final String KEY_WORKOUT_CYCLE = "cycle";
private static final String KEY_WORKOUT_NOTES = "notes";
private static final String KEY_WORKOUT_WON = "workout_won";
private static final String KEY_WORKOUT_COMPLETED = "workout_completed";
private static final String KEY_WORKOUT_EST_ONE_RM = "est_one_rm";
/**
* Extra workout table *
*/
private static final String DATABASE_TABLE_WENDLER_EXTRA = "workout_extra";
private static final String KEY_EXERCISE_NAME = "exercise_name";
private static final String KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE = "nbr_of_sets";
private static final String KEY_EXTRA_WEIGHT = "extra_weight";
private static final String KEY_EXTRA_REPS_OR_SETS_COMPLETED = "extra_reps";
private static final String KEY_EXTRA_EXERCISE_ID = "extra_exercise_id";
private static final String KEY_MAIN_EXERCISE_WEIGHT = "main_exercise_weight";
private static final String KEY_IS_STARTED = "is_started" ;
/**
* List of extra exercises, commented out ones are used but simply here for simplicity
*/
private static final String DATABASE_TABLE_WENDLER_EXTRA_LIST = "workout_extra_list";
// private static final String KEY_EXERCISE_NAME = "exercise_name";
// private static final String KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE = "nbr_of_sets";
// private static final String KEY_EXTRA_WEIGHT = "extra_weight";
// private static final String KEY_EXTRA_REPS_OR_SETS_COMPLETED = "extra_reps";
private static final String KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE
= "extra_percentage_of_main_exercise";
private static final String KEY_EXTRA_ORDER_IN_LIST = "extra_order_in_list";
private static final String KEY_EXTRA_MAIN_EXERCISE_NAME =
"extra_main_exercise_name";
private static final String KEY_IS_BBB = "is_bbb";
private final Context mContext;
private DbHelper mDbHelper;
private SQLiteDatabase mDatabase;
private boolean mIsOpen;
/**
* Constructor
*/
public SqlHandler(Context context) {
mContext = context;
}
/**
* Open the connection to the SQLite database.
*/
public void open() throws SQLException {
if (mIsOpen && mDbHelper != null && mDatabase != null) {
return;
}
mDbHelper = new DbHelper(mContext);
mDatabase = mDbHelper.getWritableDatabase();
mIsOpen = true;
}
/**
* Close the connection to the database.
*/
public void close() {
if (mIsOpen && mDbHelper != null) {
mDbHelper.close();
mIsOpen = false;
}
}
/**
* Insert one rm for all exercises as well as the workout percentage.
*/
public void insertOneRmAndWorkoutPercentage(double pressOneRm,
double deadliftOneRm,
double benchOneRm,
double squatOneRm,
int workoutPercentage) {
ContentValues cv = new ContentValues();
cv.put(KEY_1RM, pressOneRm);
cv.put(KEY_CYCLE, 1);
cv.put(KEY_CYCLE_NAME, 1);
cv.put(KEY_WEEK, 1);
cv.put(KEY_TRAINING_PERCENTAGE, workoutPercentage);
cv.put(KEY_NAME, Constants.EXERCISES[0]);
mDatabase.insert(DATABASE_TABLE_WENDLER_STATS, null, cv);
cv.clear();
cv.put(KEY_1RM, deadliftOneRm);
cv.put(KEY_CYCLE, 1);
cv.put(KEY_CYCLE_NAME, 1);
cv.put(KEY_WEEK, 1);
cv.put(KEY_TRAINING_PERCENTAGE, workoutPercentage);
cv.put(KEY_NAME, Constants.EXERCISES[1]);
mDatabase.insert(DATABASE_TABLE_WENDLER_STATS, null, cv);
cv.clear();
cv.put(KEY_1RM, benchOneRm);
cv.put(KEY_CYCLE, 1);
cv.put(KEY_CYCLE_NAME, 1);
cv.put(KEY_WEEK, 1);
cv.put(KEY_TRAINING_PERCENTAGE, workoutPercentage);
cv.put(KEY_NAME, Constants.EXERCISES[2]);
mDatabase.insert(DATABASE_TABLE_WENDLER_STATS, null, cv);
cv.clear();
cv.put(KEY_1RM, squatOneRm);
cv.put(KEY_CYCLE, 1);
cv.put(KEY_CYCLE_NAME, 1);
cv.put(KEY_WEEK, 1);
cv.put(KEY_TRAINING_PERCENTAGE, workoutPercentage);
cv.put(KEY_NAME, Constants.EXERCISES[3]);
mDatabase.insert(DATABASE_TABLE_WENDLER_STATS, null, cv);
}
/**
* Insert the order of workouts.
*/
public void insertExerciseOrder(int pressDay, int deadliftDay, int benchDay, int squatDay) {
ContentValues cv = new ContentValues();
cv.put(KEY_ORDER, pressDay);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[0]});
cv.clear();
cv.put(KEY_ORDER, deadliftDay);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[1]});
cv.clear();
cv.put(KEY_ORDER, benchDay);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[2]});
cv.clear();
cv.put(KEY_ORDER, squatDay);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[3]});
}
/**
* Insert the percentages for our workouts.
*/
public void insertWeekPercentages(
int[] weekOne, int[] weekTwo, int[] weekThree, int[] weekFour) {
ContentValues cv = new ContentValues();
cv.put(KEY_WEEK_ONE, weekOne[0]);
cv.put(KEY_WEEK_TWO, weekTwo[0]);
cv.put(KEY_WEEK_THREE, weekThree[0]);
cv.put(KEY_WEEK_FOUR, weekFour[0]);
mDatabase.insert(DATABASE_TABLE_PERCENT, null, cv);
cv.clear();
cv.put(KEY_WEEK_ONE, weekOne[1]);
cv.put(KEY_WEEK_TWO, weekTwo[1]);
cv.put(KEY_WEEK_THREE, weekThree[1]);
cv.put(KEY_WEEK_FOUR, weekFour[1]);
mDatabase.insert(DATABASE_TABLE_PERCENT, null, cv);
cv.clear();
cv.put(KEY_WEEK_ONE, weekOne[2]);
cv.put(KEY_WEEK_TWO, weekTwo[2]);
cv.put(KEY_WEEK_THREE, weekThree[2]);
cv.put(KEY_WEEK_FOUR, weekFour[2]);
mDatabase.insert(DATABASE_TABLE_PERCENT, null, cv);
}
/**
* Update the percentages of our workouts.
*/
public void updatePercentages(int[] weekOne, int[] weekTwo, int[] weekThree, int[] weekFour) {
ContentValues cv = new ContentValues();
cv.put(KEY_WEEK_ONE, weekOne[0]);
cv.put(KEY_WEEK_TWO, weekTwo[0]);
cv.put(KEY_WEEK_THREE, weekThree[0]);
cv.put(KEY_WEEK_FOUR, weekFour[0]);
mDatabase.update(DATABASE_TABLE_PERCENT, cv, KEY_ROW_ID + "=?", new String[]{"1"});
cv.clear();
cv.put(KEY_WEEK_ONE, weekOne[1]);
cv.put(KEY_WEEK_TWO, weekTwo[1]);
cv.put(KEY_WEEK_THREE, weekThree[1]);
cv.put(KEY_WEEK_FOUR, weekFour[1]);
mDatabase.update(DATABASE_TABLE_PERCENT, cv, KEY_ROW_ID + "=?", new String[]{"2"});
cv.clear();
cv.put(KEY_WEEK_ONE, weekOne[2]);
cv.put(KEY_WEEK_TWO, weekTwo[2]);
cv.put(KEY_WEEK_THREE, weekThree[2]);
cv.put(KEY_WEEK_FOUR, weekFour[2]);
mDatabase.update(DATABASE_TABLE_PERCENT, cv, KEY_ROW_ID + "=?", new String[]{"3"});
}
/**
* Insert the initial increments for workouts.
*/
public void insertIncrements(double pressIncrement,
double deadliftIncrement,
double benchIncrement,
double squatIncrement) {
ContentValues cv = new ContentValues();
cv.put(KEY_INCREMENT, pressIncrement);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[0]});
cv.clear();
cv.put(KEY_INCREMENT, deadliftIncrement);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[1]});
cv.clear();
cv.put(KEY_INCREMENT, benchIncrement);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[2]});
cv.clear();
cv.put(KEY_INCREMENT, squatIncrement);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[3]});
}
/**
* Return if the data base is initialized.
*/
public boolean isInitialized() {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_ROW_ID},
null, null, null, null, null);
return c != null && c.moveToFirst();
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Return a list of workouts for a given week.
*/
public ArrayList<Workout> getWorkoutsForList(int week) {
ArrayList<Workout> list = new ArrayList<>();
String[] names = getExerciseNamesInOrder();
for (String name : names) {
int cycle = getCurrentCycle(name);
int cycleName = getCurrentCycleName(name);
int id = exerciseHasBeenDone(name, week, cycle);
if (id != -1) {
boolean isWorkoutComplete = isWorkoutComplete(id);
list.add(new Workout(name, StringHelper.getTranslatableName(mContext, name),
isWorkoutComplete, exerciseWon(id),
week, cycle, cycleName, id, getMainExerciseForWorkout(isWorkoutComplete, id)
, getExtraExerciseForWorkout(name, id, isWorkoutComplete),
getInsertTimeForId(id), getTimeForWorkout(id), getNotesForWorkout(id)));
} else {
WendlerizedLog.d("Add: " + name + " Week: " + week + " Cycle: " + cycle);
list.add(new Workout(name, StringHelper.getTranslatableName(mContext, name),
week, cycle, cycleName, -1, getMainExerciseForWorkout(name, week)));
}
}
return list;
}
/**
* Return the names of our exercises in order converted to references a string item
*/
public String[] getExerciseNamesInOrder() {
String[] arr = new String[4];
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_NAME},
null, null, null, null, KEY_ORDER + " ASC ");
int i = 0;
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
do {
arr[i] = cursor.getString(cursor.getColumnIndex(KEY_NAME));
i++;
} while (cursor.moveToNext() && i < arr.length);
}
return arr;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return the main exercise for a given workout.
*/
public MainExercise getMainExerciseForWorkout(Workout workout) {
return getMainExerciseForWorkout(workout.getName(), workout.getWeek());
}
/**
* Return main exercise for a given week and name.
*/
public MainExercise getMainExerciseForWorkout(String name, int week) {
String[] columns =
new String[]{KEY_TRAINING_PERCENTAGE, KEY_1RM, KEY_INCREMENT, KEY_NAME};
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, columns,
KEY_NAME + "=?", new String[]{name}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
double increment = cursor.getFloat(cursor.getColumnIndex(KEY_INCREMENT));
int workoutPercentage = cursor.getInt(cursor.getColumnIndex
(KEY_TRAINING_PERCENTAGE));
double oneRm = cursor.getFloat(cursor.getColumnIndex(KEY_1RM));
int[] setPercentages = getSetPercentages(week);
boolean showWarmUp = PreferenceUtil.getBoolean(
mContext, PreferenceUtil.KEY_SHOW_WARM_UP, true);
List<SetGroup> setGroups = new ArrayList<>();
if (showWarmUp) {
String serialized = PreferenceUtil.getString(
mContext,
PreferenceUtil.KEY_WARM_UP_SETS,
WendlerConstants.DEFAULT_WARMUP_PERCENTAGES);
List<ExerciseSet> set = WendlerMath.getWarmupSets(
mContext, oneRm, serialized.split(","), -1);
setGroups.add(new SetGroup(SetType.WARM_UP, set));
}
List<ExerciseSet> set = WendlerMath.getWorkoutSets(
mContext, oneRm, setPercentages, week, -1);
ArrayList<ExerciseSet> sets = new ArrayList<>();
sets.addAll(set);
setGroups.add(new SetGroup(SetType.REGULAR, set));
int highestEstimated1RM = getHighestEstimated1RM(name);
int repsToBeat = WendlerMath.getRepsToBeat(set, highestEstimated1RM);
return new MainExercise(
name, oneRm, increment, sets, setGroups, workoutPercentage, 0, repsToBeat);
}
return null;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return the set percentages for a given week.
*/
public int[] getSetPercentages(int week) {
int[] percentages = new int[3];
String[] columns = new String[]{"percent_week_" + week};
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_PERCENT, columns, null, null, null, null,
null);
if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; i < percentages.length; i++) {
percentages[i] = cursor.getInt(cursor.getColumnIndex(columns[0]));
cursor.moveToNext();
}
}
return percentages;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return the extra exercises for a given workout.
*/
public ArrayList<AdditionalExercise> getAdditionalExercisesForWorkout(Workout workout) {
ArrayList<AdditionalExercise> exercises = new ArrayList<>();
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_EXTRA_LIST, null,
KEY_WORKOUT_EXERCISE + "=?", new String[]{workout.getName()}, null, null,
KEY_EXTRA_ORDER_IN_LIST + " ASC");
if (cursor != null && cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex(KEY_EXERCISE_NAME));
int sets = cursor.getInt(cursor.getColumnIndex
(KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE));
double weight = cursor.getDouble(cursor.getColumnIndex(KEY_EXTRA_WEIGHT));
int percentage = cursor.getInt(cursor.getColumnIndex
(KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE));
String mainExerciseName = cursor.getString(cursor.getColumnIndex
(KEY_EXTRA_MAIN_EXERCISE_NAME));
int exerciseId = cursor.getInt(cursor.getColumnIndex(KEY_EXTRA_EXERCISE_ID));
ArrayList<ExerciseSet> exerciseSets = new ArrayList<>();
double mainExerciseWeight = getOneRmForExercise(mainExerciseName);
if (!TextUtils.isEmpty(mainExerciseName)) {
weight = WendlerMath.calculateWeight(
mContext,
getOneRmForExercise(mainExerciseName),
percentage);
}
ExerciseSet set = new ExerciseSet(SetType.REGULAR, weight, sets, 0, false);
exerciseSets.add(set);
AdditionalExercise exercise = new AdditionalExercise(
name,
exerciseSets,
mainExerciseName,
percentage,
mainExerciseWeight,
exerciseId,
false);
exercises.add(exercise);
} while (cursor.moveToNext());
}
return exercises;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Get the current one rm for a given exercise.
*/
public double getOneRmForExercise(String name) {
double weight = 100;
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_1RM},
KEY_NAME + "=?", new String[]{name}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
weight = cursor.getDouble(cursor.getColumnIndex(KEY_1RM));
}
return weight;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Get the highest estimated 1rm for a given exercise
*/
public int getHighestEstimated1RM(String name) {
// TODO this is pretty inefficient, maybe add some caching?
String[] cols = new String[]{
KEY_WORKOUT_LAST_SET,
KEY_WORKOUT_REPS,
KEY_WORKOUT_EST_ONE_RM};
String selection = KEY_WORKOUT_EXERCISE + "=?" + " AND " + KEY_WORKOUT_EST_ONE_RM + ">0";
String[] selectionArguments = new String[]{name};
String orderBy = KEY_WORKOUT_EST_ONE_RM + " DESC";
String limit = "1";
int estOneRm = -1;
Cursor cursor = null;
try {
WendlerizedLog.v("Trying to get highest 1RM for exercise " + name);
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, cols, selection,
selectionArguments, null /** Group by **/, null /** Having **/, orderBy, limit);
if (cursor != null && cursor.moveToFirst()) {
estOneRm = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_EST_ONE_RM));
WendlerizedLog.v("Found highest 1RM for exercise " + name + ": " + estOneRm);
} else {
WendlerizedLog.v("Could not find highest 1RM for exercise " + name + "!");
}
} catch (Exception e) {
WendlerizedLog.v("Error while trying to find highest 1RM for exercise " + name + "!");
} finally {
if (cursor != null) {
cursor.close();
}
}
return estOneRm;
}
/**
* Return additional exercise names stored as permanent exercises.
*/
public ArrayList<String> getAdditionalExerciseNames() {
String[] column = new String[]{KEY_EXERCISE_NAME};
ArrayList<String> names = new ArrayList<>();
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_EXTRA_LIST, column, null, null,
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
names.add(cursor.getString(cursor.getColumnIndex(KEY_EXERCISE_NAME)));
} while (cursor.moveToNext());
}
return names;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return the next workout id.
*/
public int getNextWorkoutId() {
int nextWorkoutId = 1;
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, new String[]{KEY_ROW_ID},
null, null, null, null, KEY_ROW_ID + " ASC");
if (cursor != null && cursor.moveToLast()) {
nextWorkoutId = cursor.getInt(cursor.getColumnIndex(KEY_ROW_ID));
nextWorkoutId++;
}
return nextWorkoutId;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Store the main exercise and return if it was successful.
*/
public boolean storeMainExercise(Workout workout, boolean complete) {
ContentValues cv = new ContentValues();
double oneRm = workout.getMainExercise().getWeight();
int size = workout.getMainExercise().getExerciseSets().size();
double lastSet = workout.getMainExercise().getExerciseSet(size - 1).getWeight();
int estOneRm = workout.getMainExercise().getEstOneRm();
cv.put(KEY_WORKOUT_ID, workout.getWorkoutId());
cv.put(KEY_WORKOUT_EXERCISE, workout.getName());
cv.put(KEY_WORKOUT_REPS, workout.getMainExercise().getLastSetProgress());
cv.put(KEY_WORKOUT_LAST_SET, lastSet);
cv.put(KEY_WORKOUT_ONE_RM, oneRm);
cv.put(KEY_WORKOUT_WEEK, workout.getWeek());
cv.put(KEY_WORKOUT_CYCLE, workout.getCycle());
cv.put(KEY_CYCLE_NAME, workout.getCycleDisplayName());
cv.put(KEY_WORKOUT_NOTES, workout.getNotes());
cv.put(KEY_WORKOUT_WON, workout.isWon() ? 1 : 0);
cv.put(KEY_INSERT_TIME, workout.getInsertTime());
cv.put(KEY_WORKOUT_YEAR, workout.getWorkoutTime().year);
cv.put(KEY_WORKOUT_MONTH, workout.getWorkoutTime().month);
cv.put(KEY_WORKOUT_DAY, workout.getWorkoutTime().monthDay);
cv.put(KEY_TRAINING_PERCENTAGE, workout.getMainExercise().getWorkoutPercentage());
cv.put(KEY_WORKOUT_EST_ONE_RM, estOneRm);
if (!workout.isComplete()) {
cv.put(KEY_WORKOUT_COMPLETED, complete ? 1 : 0);
} else {
cv.put(KEY_WORKOUT_COMPLETED, 1);
}
if (isWorkoutNew(workout.getWorkoutId())) {
return mDatabase.insert(DATABASE_TABLE_WENDLER_WORKOUT, null, cv) != -1;
}
WendlerizedLog.d("Update! " + workout.getMainExercise().getLastSetProgress());
return mDatabase.update(DATABASE_TABLE_WENDLER_WORKOUT, cv, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workout.getWorkoutId())}) != -1;
}
/**
* Return if the workout is new.
*/
private boolean isWorkoutNew(int workoutId) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(workoutId)},
null, null, null);
return cursor != null && cursor.getCount() != 1;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Store the additional workouts for a workout.
*/
public boolean storeAdditionalExercise(
int workoutId, ArrayList<AdditionalExercise> exercises, boolean isStarted) {
mDatabase.delete(
DATABASE_TABLE_WENDLER_EXTRA, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workoutId)});
for (AdditionalExercise exercise : exercises) {
ContentValues cv = new ContentValues();
cv.put(KEY_WORKOUT_ID, workoutId);
cv.put(KEY_EXERCISE_NAME, exercise.getName());
cv.put(KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE, exercise.getExerciseSet(0).getGoal());
cv.put(KEY_EXTRA_REPS_OR_SETS_COMPLETED, exercise.getExerciseSet(0).getProgress());
cv.put(KEY_EXTRA_WEIGHT, exercise.getExerciseSet(0).getWeight());
cv.put(KEY_EXTRA_MAIN_EXERCISE_NAME, exercise.getMainExerciseName());
cv.put(KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE, exercise.getMainExercisePercentage());
cv.put(KEY_EXTRA_EXERCISE_ID, exercise.getExerciseId());
cv.put(KEY_MAIN_EXERCISE_WEIGHT, exercise.getMainExerciseWeight());
cv.put(KEY_IS_STARTED, isStarted ? 1 : 0);
mDatabase.insert(DATABASE_TABLE_WENDLER_EXTRA, null, cv);
}
if (exercises.isEmpty()) {
mDatabase.delete(DATABASE_TABLE_WENDLER_EXTRA, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workoutId)});
}
return true;
}
/**
* Update the stats for an exercise.
*/
public void updateWorkoutStats(Workout workout, boolean isNew) {
if (!workout.isComplete()) {
return;
}
if (isNew) {
updateNewWorkoutStats(workout);
} else if (isWorkoutLatest(workout)) {
updateLatestWorkoutStats(workout);
}
}
/**
* Return the increment for an exercise.
*/
public double getIncrement(String exercise) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_INCREMENT},
KEY_NAME + "=?", new String[]{exercise}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String increment = cursor.getString(cursor.getColumnIndex(KEY_INCREMENT));
if (increment == null) {
return 2.5;
}
return Double.parseDouble(increment);
}
return 2.5;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return the selection for the navigation.
*/
public int getSelectionForNavigation() {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, null,
null, null, null, null, KEY_CYCLE + " ASC, " + KEY_WEEK + " ASC");
if (cursor != null && cursor.moveToFirst()) {
return Integer.parseInt(cursor.getString(cursor.getColumnIndex(KEY_WEEK))) - 1;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return 0;
}
/**
* Store an additional exercise in the table for permanent exercises.
*/
public void storeAdditionalExercise(AdditionalExercise exercise,
String workoutExercise,
boolean isNew,
int position) {
ContentValues cv = new ContentValues();
cv.put(KEY_EXERCISE_NAME, exercise.getName());
cv.put(KEY_WORKOUT_EXERCISE, workoutExercise);
cv.put(KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE, exercise.getExerciseSet(0).getGoal());
cv.put(KEY_EXTRA_WEIGHT, exercise.getExerciseSet(0).getWeight());
cv.put(KEY_EXTRA_MAIN_EXERCISE_NAME, exercise.getMainExerciseName());
cv.put(KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE, exercise.getMainExercisePercentage());
cv.put(KEY_EXTRA_ORDER_IN_LIST, position);
cv.put(KEY_EXTRA_EXERCISE_ID, exercise.getExerciseId());
cv.put(KEY_MAIN_EXERCISE_WEIGHT, exercise.getMainExerciseWeight());
if (isNew) {
mDatabase.insert(DATABASE_TABLE_WENDLER_EXTRA_LIST, null, cv);
} else {
mDatabase.update(DATABASE_TABLE_WENDLER_EXTRA_LIST, cv,
KEY_EXTRA_EXERCISE_ID + "=? " + "AND " + KEY_WORKOUT_EXERCISE + "=?",
new String[]{String.valueOf(exercise.getExerciseId()), workoutExercise}
);
}
}
/**
* Return if a given extra exercise id already exists for a workout.
*/
public boolean extraExerciseIsNew(String workoutName, int exerciseId) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_EXTRA_LIST, null,
KEY_WORKOUT_EXERCISE + "=? AND " + KEY_EXTRA_EXERCISE_ID + "=?",
new String[]{workoutName, String.valueOf(exerciseId)}, null, null, null);
return cursor != null && cursor.getCount() == 0;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Delete an additional exercise for a given workout.
*/
public void deleteAdditionalExercise(String workoutName, AdditionalExercise exercise) {
mDatabase.delete(DATABASE_TABLE_WENDLER_EXTRA_LIST, KEY_WORKOUT_EXERCISE + "=? AND " +
KEY_EXTRA_EXERCISE_ID + "=?",
new String[]{workoutName, String.valueOf(exercise.getExerciseId())}
);
}
/**
* Reorder the additional exercises for a given workout.
*/
public void doReorderAdditionalExercise(ArrayList<AdditionalExercise> exercises,
String workoutName) {
for (int i = 0; i < exercises.size(); i++) {
ContentValues values = new ContentValues();
values.put(KEY_EXTRA_ORDER_IN_LIST, i);
mDatabase.update(DATABASE_TABLE_WENDLER_EXTRA_LIST, values,
KEY_EXTRA_EXERCISE_ID + "=? AND " + KEY_WORKOUT_EXERCISE + "=?",
new String[]{String.valueOf(exercises.get(i).getExerciseId()), workoutName});
}
}
/**
* Return a list of old workouts.
*/
public ArrayList<Workout> getOldWorkouts(int limit) {
ArrayList<Workout> workouts = new ArrayList<>();
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_COMPLETED + "=?", new String[]{"1"}, null, null,
KEY_INSERT_TIME + " DESC", String.valueOf(limit));
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_EXERCISE));
boolean isWon =
cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_WON)) == 1;
int week = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_WEEK));
int cycle = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_CYCLE));
int cycleName = cursor.getInt(cursor.getColumnIndex(KEY_CYCLE_NAME));
int id = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_ID));
MainExercise mainExercise = getMainExerciseForWorkout(true, id);
ArrayList<AdditionalExercise> additionalExercises =
getExtraExerciseForWorkout(name, id, true);
long insertTime = Long.parseLong(
cursor.getString(cursor.getColumnIndex(KEY_INSERT_TIME)));
Time time = new Time();
time.year = Integer.parseInt(
cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_YEAR)));
time.month = Integer.parseInt(
cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_MONTH)));
time.monthDay = Integer.parseInt(
cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_DAY)));
String notes = cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_NOTES));
Workout workout = new Workout(name, StringHelper.getTranslatableName(mContext,
name), true, isWon, week, cycle, cycleName, id,
mainExercise, additionalExercises, insertTime, time, notes
);
workouts.add(workout);
} while (cursor.moveToNext());
}
return workouts;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Delete an old workout.
*/
public void deleteWorkout(Workout workout) {
if (isWorkoutLatest(workout)) {
int week = workout.getWeek();
int cycle = workout.getCycle();
int cycleName = workout.getCycleDisplayName();
double weight = workout.getMainExercise().getWeight();
if (workout.isWon() && workout.getWeek() == 4) {
weight = getOneRmForExercise(workout.getName());
weight -= getIncrement(workout.getName());
}
updateWithStats(workout.getName(), cycle, week, weight, cycleName);
setWorkoutShouldDeload(workout.getName(), getWorkoutShouldDeload(workout));
}
mDatabase.delete(DATABASE_TABLE_WENDLER_WORKOUT, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workout.getWorkoutId())});
mDatabase.delete(DATABASE_TABLE_WENDLER_EXTRA, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workout.getWorkoutId())});
}
/**
* Return the number of old workouts.
*/
public int getOldWorkoutsCount() {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_COMPLETED + "=?", new String[]{"1"}, null,
null, null);
return cursor == null ? 0 : cursor.getCount();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Store a workout.
*/
public void storeWorkout(Workout workout) {
storeMainExercise(workout, workout.isComplete());
storeAdditionalExercise(workout.getWorkoutId(), workout.getAdditionalExercises(), true);
if (isWorkoutLatest(workout)) {
updateWorkoutStats(workout, true);
}
}
/**
* Update the one rm maximum for all exercises.
*/
public void updateOneRm(
double pressOneRm, double deadliftOneRm, double benchOneRm, double squatOneRm) {
ContentValues cv = new ContentValues();
cv.put(KEY_1RM, pressOneRm);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[0]});
cv.clear();
cv.put(KEY_1RM, deadliftOneRm);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[1]});
cv.clear();
cv.put(KEY_1RM, benchOneRm);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[2]});
cv.clear();
cv.put(KEY_1RM, squatOneRm);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[3]});
}
/**
* Delete all extra exercises.
*/
public void purgeExtraExercises() {
mDatabase.delete(DATABASE_TABLE_WENDLER_EXTRA_LIST, null, null);
}
/**
* Return if the workout should deload.
*/
public boolean doDeload(Workout workout) {
if (workout.getWeek() < 4) {
return false;
}
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, null,
KEY_NAME + "=? AND " + KEY_SHOULD_DELOAD + "=?",
new String[]{workout.getName(), "1"}, null, null, null);
boolean bool = cursor != null && cursor.getCount() == 1;
if (bool) {
setWorkoutShouldDeload(workout.getName(), false);
}
return bool;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Update the cycle name for all workouts.
*/
public void updateCycleName() {
ContentValues cv = new ContentValues();
int cycle = getCurrentCycle(Constants.EXERCISES[0]);
cv.put(KEY_CYCLE_NAME, cycle);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[0]});
cv.clear();
cycle = getCurrentCycle(Constants.EXERCISES[1]);
cv.put(KEY_CYCLE_NAME, cycle);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[1]});
cv.clear();
cycle = getCurrentCycle(Constants.EXERCISES[2]);
cv.put(KEY_CYCLE_NAME, cycle);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[2]});
cv.clear();
cycle = getCurrentCycle(Constants.EXERCISES[3]);
cv.put(KEY_CYCLE_NAME, cycle);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{Constants.EXERCISES[3]});
}
/**
* Restore the database from a save file.
*/
public void restoreDbFromFile(File file) {
mDbHelper.restoreDatabase(file);
}
/**
* Return the workout percentage for a given exercise.
*/
public int getWorkoutPercentage(String workoutName) {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_TRAINING_PERCENTAGE},
KEY_NAME + "=?", new String[]{workoutName}, null, null, null);
if (c != null && c.moveToFirst()) {
return c.getInt(c.getColumnIndex(KEY_TRAINING_PERCENTAGE));
}
} finally {
if (c != null) {
c.close();
}
}
return WendlerConstants.DEFAULT_WORKOUT_PERCENTAGE;
}
/**
* Update the old workouts to reflect using Training Max.
*/
public void updateOldWorkouts() {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT,
new String[]{KEY_WORKOUT_EXERCISE, KEY_1RM, KEY_WORKOUT_ID},
KEY_WORKOUT_COMPLETED + "=?", new String[]{String.valueOf(1)}, null, null,
null, null);
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_EXERCISE));
double oneRm = cursor.getFloat(cursor.getColumnIndex(KEY_1RM));
int workoutId = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_ID));
updateOneRm(name, oneRm, workoutId);
} while (cursor.moveToNext());
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Update the old workouts with the new TM
*/
private void updateOneRm(String workoutName, double oneRm, int workoutId) {
ContentValues value = new ContentValues();
value.put(KEY_1RM, WendlerMath.calculateWeight(
mContext,
oneRm,
getWorkoutPercentage(workoutName)));
mDatabase.update(DATABASE_TABLE_WENDLER_WORKOUT, value, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workoutId)});
}
/**
* Return if the next workout should deload.
*/
private boolean getWorkoutShouldDeload(Workout workout) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_EXERCISE + "=? AND " + KEY_WORKOUT_COMPLETED + "=?",
new String[]{workout.getName(), "1"}, null,
null, KEY_CYCLE + " DESC, " + KEY_WEEK + " DESC");
return cursor != null
&& cursor.moveToPosition(1)
&& cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_WON)) == 0;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return if a given workout is complete.
*/
private boolean isWorkoutComplete(int id) {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(id)}, null, null, null);
return c != null
&& c.moveToFirst()
&& c.getInt(c.getColumnIndex(KEY_WORKOUT_COMPLETED)) == 1;
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Return if a given workout was won.
*/
private boolean exerciseWon(int id) {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(id)}, null, null, null);
return c != null
&& c.moveToFirst()
&& c.getInt(c.getColumnIndex(KEY_WORKOUT_WON)) == 1;
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Return if a given workout for a specific cycle and week has been done.
*/
private int exerciseHasBeenDone(String sqlExerciseName, int week, int cycle) {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, new String[]{KEY_WORKOUT_ID},
KEY_WORKOUT_EXERCISE + "=? AND " + KEY_CYCLE + "=? AND " + KEY_WEEK + "=?",
new String[]{sqlExerciseName, String.valueOf(cycle), String.valueOf(week)},
null, null, null);
if (c != null && c.moveToFirst()) {
return c.getInt(c.getColumnIndex(KEY_WORKOUT_ID));
}
} finally {
if (c != null) {
c.close();
}
}
return -1;
}
/**
* Return the current cycle for a given exercise.
*/
private int getCurrentCycle(String sqlExerciseName) {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_CYCLE},
KEY_NAME + "=?", new String[]{sqlExerciseName}, null, null, null);
if (c != null && c.moveToFirst()) {
return c.getInt(c.getColumnIndex(KEY_CYCLE));
}
} finally {
if (c != null) {
c.close();
}
}
return 1;
}
/**
* Return the current cycle name for a given exercise.
*/
private int getCurrentCycleName(String sqlExerciseName) {
Cursor c = null;
try {
c = mDatabase.query(DATABASE_TABLE_WENDLER_STATS, new String[]{KEY_CYCLE_NAME},
KEY_NAME + "=?", new String[]{sqlExerciseName}, null, null, null);
if (c != null && c.moveToFirst()) {
return c.getInt(c.getColumnIndex(KEY_CYCLE_NAME));
}
} finally {
if (c != null) {
c.close();
}
}
return 1;
}
/**
* Return the insert time for a given workout id.
*/
private long getInsertTimeForId(int workoutId) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT,
new String[]{KEY_INSERT_TIME}, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workoutId)}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return Long.parseLong(cursor.getString(cursor.getColumnIndex(KEY_INSERT_TIME)));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return System.currentTimeMillis();
}
/**
* Return the time the workout was completed for a given workout id.
*/
private Time getTimeForWorkout(int workoutId) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT,
new String[]{KEY_WORKOUT_YEAR, KEY_WORKOUT_MONTH, KEY_WORKOUT_DAY},
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(workoutId)}, null, null,
null);
Time time = new Time();
time.setToNow();
if (cursor != null && cursor.moveToFirst()) {
time.year =
Integer.parseInt(cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_YEAR)));
time.month = Integer.parseInt(
cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_MONTH)));
time.monthDay =
Integer.parseInt(cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_DAY)));
}
return time;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return the notes for a given workout id.
*/
private String getNotesForWorkout(int workoutId) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT,
new String[]{KEY_WORKOUT_NOTES}, KEY_WORKOUT_ID + "=?",
new String[]{String.valueOf(workoutId)}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_NOTES));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return "";
}
/**
* Return the main exercise for a given workout id.
*/
private MainExercise getMainExerciseForWorkout(boolean isComplete, int workoutId) {
MainExercise mainExercise = null;
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(workoutId)},
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int week = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_WEEK));
String name = cursor.getString(cursor.getColumnIndex(KEY_WORKOUT_EXERCISE));
int repsPerformed = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_REPS));
double increment = getIncrement(name);
int workoutPercentage =
cursor.getInt(cursor.getColumnIndex(KEY_TRAINING_PERCENTAGE));
double oneRm = cursor.getFloat(cursor.getColumnIndex(KEY_1RM));
int[] setPercentages = getSetPercentages(week);
boolean showWarmUp = PreferenceUtil.getBoolean(
mContext, PreferenceUtil.KEY_SHOW_WARM_UP, true);
List<SetGroup> setGroups = new ArrayList<>();
if (showWarmUp) {
String serialized = PreferenceUtil.getString(
mContext,
PreferenceUtil.KEY_WARM_UP_SETS,
WendlerConstants.DEFAULT_WARMUP_PERCENTAGES);
List<ExerciseSet> set = WendlerMath.getWarmupSets(
mContext,
oneRm,
serialized.split(","),
repsPerformed);
ArrayList<ExerciseSet> sets = new ArrayList<>();
sets.addAll(set);
setGroups.add(new SetGroup(SetType.WARM_UP, sets));
}
List<ExerciseSet> set = WendlerMath.getWorkoutSets(
mContext, oneRm, setPercentages, week, repsPerformed);
ArrayList<ExerciseSet> sets = new ArrayList<>();
sets.addAll(set);
setGroups.add(new SetGroup(SetType.REGULAR, sets));
int estOneRm = cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_EST_ONE_RM));
int repsToBeat = -1; // Workout has been done, so no need to show the reps-to-beat
mainExercise = new MainExercise(name, oneRm, increment, sets, setGroups,
workoutPercentage, estOneRm, repsToBeat);
}
if (!isComplete && mainExercise != null && mainExercise.getLastSetProgress() < 0) {
return null;
}
return mainExercise;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Return a list of additional exercises for a given workout id.
*/
private ArrayList<AdditionalExercise> getExtraExerciseForWorkout(
String workoutName, int workoutId, boolean isComplete) {
ArrayList<AdditionalExercise> exercises = new ArrayList<>();
boolean isStarted = false;
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_EXTRA, null,
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(workoutId)},
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex(KEY_EXERCISE_NAME));
int sets = cursor.getInt(cursor.getColumnIndex
(KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE));
double weight = cursor.getDouble(cursor.getColumnIndex(KEY_EXTRA_WEIGHT));
int repsPerformed = cursor.getInt(cursor.getColumnIndex
(KEY_EXTRA_REPS_OR_SETS_COMPLETED));
isStarted = cursor.getInt(cursor.getColumnIndex(KEY_IS_STARTED)) == 1;
if (!isStarted && repsPerformed > 0) {
isStarted = true;
}
int exerciseId = cursor.getInt(cursor.getColumnIndex(KEY_EXTRA_EXERCISE_ID));
String mainExerciseName = cursor.getString(cursor.getColumnIndex
(KEY_EXTRA_MAIN_EXERCISE_NAME));
int percentage = cursor.getInt(cursor.getColumnIndex
(KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE));
ArrayList<ExerciseSet> exerciseSets = new ArrayList<>();
double mainExerciseWeight = cursor.getDouble(
cursor.getColumnIndex(KEY_MAIN_EXERCISE_WEIGHT));
if (!TextUtils.isEmpty(mainExerciseName)) {
weight = WendlerMath.calculateWeight(
mContext,
getOneRmForExercise(mainExerciseName),
percentage);
}
ExerciseSet set = new ExerciseSet(SetType.REGULAR, weight, sets, repsPerformed);
exerciseSets.add(set);
AdditionalExercise exercise = new AdditionalExercise(
name,
exerciseSets,
mainExerciseName,
percentage,
mainExerciseWeight,
exerciseId,
isStarted);
exercises.add(exercise);
} while (cursor.moveToNext());
}
ArrayList<AdditionalExercise> original = getAdditionalExercisesForWorkout(
new Workout(
workoutName,
StringHelper.getTranslatableName(mContext, workoutName)));
if (isComplete || isStarted) {
return exercises;
} else {
return original;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Update the workout stats for a new workout.
*/
private void updateNewWorkoutStats(Workout workout) {
int currentCycle;
int cycleName = workout.getCycleDisplayName();
int week;
double increment = getIncrement(workout.getName());
double weight = getOneRmForExercise(workout.getName());
currentCycle = workout.getCycle();
week = workout.getWeek();
if (workout.isComplete() && workout.isWon()) {
if (week < 4) {
week++;
} else {
week = 1;
currentCycle++;
weight += increment;
cycleName++;
}
} else if (workout.isComplete()) {
DeloadItem deloadItem = new DeloadItem(week, currentCycle, weight, cycleName);
deloadItem = WendlerMath.doDeload(
mContext,
deloadItem,
workout.getMainExercise().getWorkoutPercentage(),
increment);
week = deloadItem.getWeek();
currentCycle = deloadItem.getCycle();
weight = deloadItem.getWeight();
setWorkoutShouldDeload(workout.getName(), deloadItem.doDelayedDeload());
cycleName = deloadItem.getCycleName();
}
updateWithStats(workout.getName(), currentCycle, week, weight, cycleName);
}
/**
* Update the latest workout stats.
*/
private void updateLatestWorkoutStats(Workout workout) {
int currentCycle;
int cycleName;
int week;
double increment = getIncrement(workout.getName());
double weight;
boolean hasWorkoutOutcomeChanged = hasWorkoutOutComeChanged(workout);
if (hasWorkoutOutcomeChanged) {
currentCycle = workout.getCycle();
cycleName = workout.getCycleDisplayName();
weight = getOneRmForExercise(workout.getName());
week = workout.getWeek();
if (workout.isComplete() && workout.isWon()) {
week++;
weight = WendlerMath.calculateNewWeight(weight, mContext, increment, true);
} else if (workout.isComplete()) {
week = 1;
currentCycle++;
cycleName++;
weight = WendlerMath.calculateNewWeight(weight, mContext, increment, false);
}
updateWithStats(workout.getName(), currentCycle, week, weight, cycleName);
}
}
/**
* Set a given workout to deload when next possible.
*/
private void setWorkoutShouldDeload(String workoutName, boolean value) {
ContentValues cv = new ContentValues();
cv.put(KEY_SHOULD_DELOAD, value ? 1 : 0);
mDatabase.update(DATABASE_TABLE_WENDLER_STATS, cv, KEY_NAME + "=?",
new String[]{workoutName});
}
/**
* Return if the outcome has changed for a given workout.
*/
private boolean hasWorkoutOutComeChanged(Workout workout) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_ID + "=?", new String[]{String.valueOf(workout.getWorkoutId())},
null, null, null);
return cursor != null && cursor.moveToFirst() &&
(cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_WON)) == 1) != workout.isWon();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Update a exercise with given stats.
*/
private void updateWithStats(
String name, int cycle, int week, double weight, int cycleName) {
if (cycle < 1) {
cycle = 1;
cycleName = 1;
}
ContentValues contentValues = new ContentValues();
contentValues.put(KEY_WORKOUT_ONE_RM, weight);
contentValues.put(KEY_WEEK, String.valueOf(week));
contentValues.put(KEY_CYCLE, String.valueOf(cycle));
contentValues.put(KEY_CYCLE_NAME, String.valueOf(cycleName));
mDatabase.update(
DATABASE_TABLE_WENDLER_STATS, contentValues, KEY_NAME + "=?", new String[]{name});
}
/**
* Return if the workout is the latest performed.
*/
private boolean isWorkoutLatest(Workout workout) {
Cursor cursor = null;
try {
cursor = mDatabase.query(DATABASE_TABLE_WENDLER_WORKOUT, null,
KEY_WORKOUT_EXERCISE + "=? AND " + KEY_WORKOUT_COMPLETED + "=?",
new String[]{workout.getName(), "1"}, null,
null, KEY_CYCLE + " DESC, " + KEY_WEEK + " DESC");
return cursor != null
&& cursor.moveToFirst()
&& workout.getWorkoutId()
== cursor.getInt(cursor.getColumnIndex(KEY_WORKOUT_ID));
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Inner class for handling the connection to the SQLiteDatabase.
*/
private static class DbHelper extends SQLiteOpenHelper {
private final Context mContext;
/**
* Constructor.
*/
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
/**
* Called when the helper is created.
*/
@Override
public void onCreate(SQLiteDatabase db) {
/**
* Table for managing all the percentages for the 4 weeks.
*/
db.execSQL("CREATE TABLE " + DATABASE_TABLE_PERCENT + " ("
+ KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ KEY_WEEK_ONE + " INTEGER NOT NULL, "
+ KEY_WEEK_TWO + " INTEGER NOT NULL, "
+ KEY_WEEK_THREE + " INTEGER NOT NULL, "
+ KEY_WEEK_FOUR + " INTEGER NOT NULL);");
/**
* Table for managing the stats for each workout.
*/
db.execSQL("CREATE TABLE " + DATABASE_TABLE_WENDLER_STATS + " (" +
KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_TRAINING_PERCENTAGE + " INTEGER NOT NULL, " +
KEY_1RM + " INTEGER NOT NULL, " +
KEY_INCREMENT + " TEXT, " +
KEY_WEEK + " TEXT, " +
KEY_CYCLE + " TEXT, " +
KEY_CYCLE_NAME + " TEXT, " +
KEY_ORDER + " INTEGER, " +
KEY_SHOULD_DELOAD + " INTEGER, " +
KEY_NAME + " TEXT);");
/**
* Table for saving the main exercise of a workout.
*/
db.execSQL("CREATE TABLE " + DATABASE_TABLE_WENDLER_WORKOUT + " (" +
KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_INSERT_TIME + " TEXT NOT NULL, " +
KEY_WORKOUT_YEAR + " INTEGER NOT NULL, " +
KEY_WORKOUT_MONTH + " INTEGER NOT NULL, " +
KEY_WORKOUT_DAY + " INTEGER NOT NULL, " +
KEY_WORKOUT_EXERCISE + " TEXT NOT NULL, " +
KEY_WORKOUT_REPS + " TEXT NOT NULL, " +
KEY_WORKOUT_ONE_RM + " TEXT NOT NULL, " +
KEY_WORKOUT_LAST_SET + " TEXT NOT NULL, " +
KEY_WORKOUT_WEEK + " TEXT NOT NULL, " +
KEY_WORKOUT_CYCLE + " TEXT NOT NULL, " +
KEY_CYCLE_NAME + " TEXT NOT NULL, " +
KEY_WORKOUT_ID + " INTEGER NOT NULL, " +
KEY_WORKOUT_NOTES + " TEXT NOT NULL, " +
KEY_WORKOUT_COMPLETED + " INTEGER NOT NULL, " +
KEY_TRAINING_PERCENTAGE + " INTEGER NOT NULL, " +
KEY_WORKOUT_WON + " INTEGER, " +
KEY_WORKOUT_EST_ONE_RM + " INTEGER) ;");
/**
* Table for managing extra exercises for a specific workout.
*/
db.execSQL("CREATE TABLE " + DATABASE_TABLE_WENDLER_EXTRA + " (" +
KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_WORKOUT_ID + " INTEGER NOT NULL, " +
KEY_EXERCISE_NAME + " TEXT NOT NULL, " +
KEY_EXTRA_EXERCISE_ID + " INTEGER NOT NULL, " +
KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE + " TEXT NOT NULL, " +
KEY_EXTRA_WEIGHT + " TEXT NOT NULL, " +
KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE + " TEXT, " +
KEY_EXTRA_MAIN_EXERCISE_NAME + " TEXT, " +
KEY_MAIN_EXERCISE_WEIGHT + " TEXT, " +
KEY_IS_STARTED + " TEXT, " +
KEY_EXTRA_REPS_OR_SETS_COMPLETED + " TEXT NOT NULL);");
/**
* Table for storing all extra exercises used so they can be reused.
*/
db.execSQL("CREATE TABLE " + DATABASE_TABLE_WENDLER_EXTRA_LIST + " (" +
KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_EXERCISE_NAME + " TEXT NOT NULL, " +
KEY_EXTRA_EXERCISE_ID + " TEXT NOT NULL, " +
KEY_WORKOUT_EXERCISE + " TEXT NOT NULL, " +
KEY_NUMBER_OF_SETS_OR_REPS_TO_BE_DONE + " TEXT NOT NULL, " +
KEY_EXTRA_WEIGHT + " TEXT, " +
KEY_EXTRA_PERCENTAGE_OF_MAIN_EXERCISE + " TEXT, " +
KEY_EXTRA_MAIN_EXERCISE_NAME + " TEXT, " +
KEY_INCREMENT + " TEXT, " +
KEY_MAIN_EXERCISE_WEIGHT + " TEXT, " +
KEY_IS_BBB + " INTEGER, " +
KEY_EXTRA_ORDER_IN_LIST + " TEXT NOT NULL);");
}
/**
* Called when the database is upgraded.
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA_LIST + " ADD COLUMN " +
KEY_IS_BBB + " INTEGER DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + KEY_IS_BBB + " in " +
DATABASE_TABLE_WENDLER_EXTRA_LIST);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_STATS + " ADD COLUMN "
+ KEY_SHOULD_DELOAD + " INTEGER DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + KEY_SHOULD_DELOAD + " in " +
DATABASE_TABLE_WENDLER_STATS);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_STATS + " ADD COLUMN "
+ KEY_CYCLE_NAME + " INTEGER DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + KEY_CYCLE_NAME + " in " +
DATABASE_TABLE_WENDLER_STATS);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_WORKOUT + " ADD COLUMN "
+ KEY_CYCLE_NAME + " INTEGER DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + KEY_CYCLE_NAME + " in " +
DATABASE_TABLE_WENDLER_WORKOUT);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA + " ADD COLUMN " +
KEY_MAIN_EXERCISE_WEIGHT + " TEXT DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + KEY_MAIN_EXERCISE_WEIGHT + " in " +
DATABASE_TABLE_WENDLER_EXTRA);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA_LIST + " ADD COLUMN " +
KEY_MAIN_EXERCISE_WEIGHT + " TEXT DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + KEY_MAIN_EXERCISE_WEIGHT + " in " +
DATABASE_TABLE_WENDLER_EXTRA_LIST);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA + " ADD COLUMN " +
KEY_IS_STARTED + " TEXT DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + DATABASE_TABLE_WENDLER_EXTRA + " in " +
KEY_IS_STARTED);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA_LIST + " ADD COLUMN " +
KEY_INCREMENT + " TEXT DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + DATABASE_TABLE_WENDLER_EXTRA_LIST
+ " in " + KEY_INCREMENT);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA_LIST + " ADD COLUMN " +
KEY_IS_BBB + " INTEGER DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + DATABASE_TABLE_WENDLER_EXTRA_LIST
+ " in " + KEY_IS_BBB);
}
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_EXTRA + " ADD COLUMN " +
KEY_IS_STARTED + " INTEGER DEFAULT 0");
} catch (Exception e) {
WendlerizedLog.v("Failed to add column " + DATABASE_TABLE_WENDLER_EXTRA
+ " in " + KEY_IS_STARTED);
}
if (oldVersion < 12 && newVersion >= 12) {
try {
db.execSQL("ALTER TABLE " + DATABASE_TABLE_WENDLER_WORKOUT + " ADD COLUMN " +
KEY_WORKOUT_EST_ONE_RM + " INTEGER DEFAULT 0");
setEst1rmForExistingWorkouts(db);
} catch(Exception e) {
WendlerizedLog.v("Failed to add column " + DATABASE_TABLE_WENDLER_WORKOUT
+ " in " + KEY_WORKOUT_EST_ONE_RM);
}
}
}
/**
* Restore the database from a file.
*/
@SuppressWarnings("ConstantConditions")
public void restoreDatabase(File file) {
close();
File oldDb = new File(String.valueOf(mContext.getDatabasePath(getDatabaseName())));
if (file.exists()) {
try {
copyFile(new FileInputStream(file), new FileOutputStream(oldDb));
} catch (IOException e) {
e.printStackTrace();
}
getWritableDatabase().close();
}
}
/**
* Copy a given file.
*/
private static void copyFile(FileInputStream fromFile, FileOutputStream toFile)
throws IOException {
FileChannel fromChannel = null;
FileChannel toChannel = null;
try {
fromChannel = fromFile.getChannel();
toChannel = toFile.getChannel();
fromChannel.transferTo(0, fromChannel.size(), toChannel);
} finally {
try {
if (fromChannel != null) {
fromChannel.close();
}
} finally {
if (toChannel != null) {
toChannel.close();
}
}
}
}
private static void setEst1rmForExistingWorkouts(SQLiteDatabase database)
throws android.database.SQLException{
String format =
"UPDATE %1$s SET %2$s =" +
" CASE WHEN %4$s > 0" +
" THEN %3$s * %4$s * 0.0333 + %3$s" +
" ELSE -1" +
" END" ;
Object[] args = {DATABASE_TABLE_WENDLER_WORKOUT, KEY_WORKOUT_EST_ONE_RM,
KEY_WORKOUT_LAST_SET, KEY_WORKOUT_REPS};
String sql = String.format(Locale.ROOT, format, args);
database.execSQL(sql);
}
}
}