diff --git a/.gitignore b/.gitignore index f16637ad..752cafe6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ api.xml api.properties fabric.properties gradle.properties +google-services.json # Built application files *.apk diff --git a/app/build.gradle b/app/build.gradle index 34492337..5809b4d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.application' -apply plugin: 'me.tatarka.retrolambda' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' @@ -9,7 +8,7 @@ apply plugin: 'io.fabric' android { dexOptions { - incremental true + incremental false maxProcessCount 4 } @@ -17,12 +16,13 @@ android { buildToolsVersion '23.0.3' defaultConfig { applicationId 'com.bodyweight.fitness' - minSdkVersion 14 + minSdkVersion 16 targetSdkVersion 23 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_6 + targetCompatibility JavaVersion.VERSION_1_6 } packagingOptions { exclude 'META-INF/DEPENDENCIES.txt' @@ -40,14 +40,14 @@ android { productFlavors { pro { applicationId 'com.bodyweight.fitness.pro' - versionCode 122 - versionName "1.2.2" + versionCode 133 + versionName "1.3.0" } free { applicationId 'com.bodyweight.fitness.free' - versionCode 122 - versionName "1.2.2" + versionCode 133 + versionName "1.3.0" } } buildTypes { @@ -64,6 +64,7 @@ android { sourceSets { main.java.srcDirs += 'src/main/java' main.java.srcDirs += 'src/main/kotlin' + androidTest.java.srcDirs += 'src/androidTest/kotlin' test.java.srcDirs += 'src/test/kotlin' } } @@ -80,9 +81,13 @@ dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'org.jetbrains.spek:spek:1.0.9' -// testCompile "com.nhaarman:mockito-kotlin:0.3.0" testCompile 'org.mockito:mockito-all:2.0.2-beta' + androidTestCompile 'com.android.support:support-annotations:23.3.0' + androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:rules:0.5' + androidTestCompile "com.android.support.test.espresso:espresso-core:2.2.2" + compile 'com.android.support:support-v4:23.3.0' compile 'com.android.support:recyclerview-v7:23.3.0' compile 'com.android.support:cardview-v7:23.3.0' @@ -92,23 +97,22 @@ dependencies { compile 'commons-io:commons-io:2.4' compile 'net.danlew:android.joda:2.9.3' - compile 'com.github.bumptech.glide:glide:3.7.0' - - compile 'com.jakewharton:butterknife:6.1.0' - kapt 'com.jakewharton:butterknife:6.1.0' compile 'com.netflix.rxjava:rxjava-core:0.20.7' compile 'io.reactivex:rxandroid:1.2.0' compile 'io.reactivex:rxjava:1.1.5' - compile 'com.trello:rxlifecycle:0.5.0' - compile 'com.trello:rxlifecycle-components:0.5.0' - compile 'com.trello:rxlifecycle-kotlin:0.5.0' + compile 'com.trello:rxlifecycle:0.6.1' + compile 'com.trello:rxlifecycle-components:0.6.1' + compile 'com.trello:rxlifecycle-kotlin:0.6.1' + + compile 'com.robinhood.spark:spark:1.1.0' compile 'com.github.johnkil.android-robototextview:robototextview:2.5.0' compile 'com.gordonwong:material-sheet-fab:1.2.1' - compile 'com.github.bmarrdev:android-DecoView-charting:v1.0.1' compile 'io.github.kobakei:ratethisapp:1.0.3' + compile 'com.github.paolorotolo:appintro:4.0.0' + compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') { transitive = true; } @@ -118,7 +122,7 @@ dependencies { } buildscript { - ext.kotlin_version = '1.0.1' + ext.kotlin_version = '1.0.1-2' repositories { mavenCentral() maven { diff --git a/app/src/androidTest/kotlin/com/bodyweight/fitness/MainActivityTest.kt b/app/src/androidTest/kotlin/com/bodyweight/fitness/MainActivityTest.kt new file mode 100644 index 00000000..9cd3d780 --- /dev/null +++ b/app/src/androidTest/kotlin/com/bodyweight/fitness/MainActivityTest.kt @@ -0,0 +1,64 @@ +package com.bodyweight.fitness + +import android.support.test.espresso.Espresso.onView +import android.support.test.espresso.action.ViewActions.click +import android.support.test.espresso.assertion.ViewAssertions.matches +import android.support.test.espresso.matcher.ViewMatchers +import android.support.test.espresso.matcher.ViewMatchers.* +import android.test.ActivityInstrumentationTestCase2 + +import com.bodyweight.fitness.ui.MainActivity + +class MainActivityTest : ActivityInstrumentationTestCase2(MainActivity::class.java) { + override fun setUp() { + super.setUp() + + activity + } + + fun testExerciseSetup() { + onView(withId(R.id.toolbar_exercise_title)) + .check(matches(withText("Shoulder Rolls"))) + onView(withId(R.id.toolbar_section_title)) + .check(matches(withText("Dynamic Stretches"))) + onView(withId(R.id.toolbar_exercise_description)) + .check(matches(withText("1x(5-10)"))) + + onView(withId(R.id.prev_exercise_button)) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE))); + onView(withId(R.id.next_exercise_button)) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + + onView(withId(R.id.next_exercise_button)).perform(click()) + + onView(withId(R.id.toolbar_exercise_title)) + .check(matches(withText("Scapular Shrugs"))) + onView(withId(R.id.toolbar_section_title)) + .check(matches(withText("Dynamic Stretches"))) + onView(withId(R.id.toolbar_exercise_description)) + .check(matches(withText("1x(5-10)"))) + + onView(withId(R.id.prev_exercise_button)) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + onView(withId(R.id.next_exercise_button)) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + + onView(withId(R.id.next_exercise_button)).perform(click()) + onView(withId(R.id.next_exercise_button)).perform(click()) + onView(withId(R.id.next_exercise_button)).perform(click()) + onView(withId(R.id.next_exercise_button)).perform(click()) + onView(withId(R.id.next_exercise_button)).perform(click()) + + onView(withId(R.id.toolbar_exercise_title)) + .check(matches(withText("Front and Side Leg Swings"))) + onView(withId(R.id.toolbar_section_title)) + .check(matches(withText("Dynamic Stretches"))) + onView(withId(R.id.toolbar_exercise_description)) + .check(matches(withText("1x(5-10)"))) + + onView(withId(R.id.prev_exercise_button)) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + onView(withId(R.id.next_exercise_button)) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 898f12a5..6846746f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,21 @@ android:theme="@style/AppTheme" android:name=".App"> + + + + + + + @@ -50,6 +65,16 @@ android:value=".ui.MainActivity" /> + + + + + mSections = new ArrayList<>(); - - public Category(String categoryId, String title) { - mCategoryId = categoryId; - mTitle = title; - } - - public String getCategoryId() { - return mCategoryId; - } - - public String getTitle() { - return mTitle; - } - - public RoutineType getType() { - return RoutineType.CATEGORY; - } - - public void insertSection(Section section) { - section.setCategory(this); - - mSections.add(section); - } - - public ArrayList
getSections() { - return mSections; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/Exercise.java b/app/src/main/java/com/bodyweight/fitness/model/Exercise.java deleted file mode 100644 index 4b0825a1..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/Exercise.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.bodyweight.fitness.model; - -import java.io.Serializable; - -public class Exercise extends LinkedRoutine implements Serializable { - private String mExerciseId; - private String mId; - private String mLevel; - private String mTitle; - private String mDescription; - private String mYouTubeId; - private String mGifId; - private String mGifUrl; - private String mDefaultSet; - - private Category mCategory; - private Section mSection; - - private Exercise mPrevious; - private Exercise mNext; - - public Exercise( - String exerciseId, - String id, - String level, - String title, - String description, - String youTubeId, - String gifId, - String gifUrl, - String defaultSet) { - mExerciseId = exerciseId; - mId = id; - mLevel = level; - mTitle = title; - mDescription = description; - mYouTubeId = youTubeId; - mGifId = gifId; - mGifUrl = gifUrl; - mDefaultSet = defaultSet; - } - - public String getExerciseId() { - return mExerciseId; - } - - public String getId() { - return mId; - } - - public String getLevel() { - return mLevel; - } - - public String getTitle() { - return mTitle; - } - - public RoutineType getType() { - return RoutineType.EXERCISE; - } - - public String getDescription() { - return mDescription; - } - - public String getYouTubeId() { - return mYouTubeId; - } - - public String getGifId() { - return mGifId; - } - - public String getGifUrl() { - return mGifUrl; - } - - public boolean isTimedSet() { - return mDefaultSet.equalsIgnoreCase("timed"); - } - - public String getDefaultSet() { - return mDefaultSet; - } - - public boolean hasProgressions() { - return (getSection().getSectionMode().equals(SectionMode.LEVELS) || getSection().getSectionMode().equals(SectionMode.PICK)); - } - - public void setCategory(Category category) { - mCategory = category; - } - - public Category getCategory() { - return mCategory; - } - - public void setSection(Section section) { - mSection = section; - } - - public Section getSection() { - return mSection; - } - - public boolean isPrevious() { - return mPrevious != null; - } - - public void setPrevious(Exercise previous) { - mPrevious = previous; - } - - public Exercise getPrevious() { - return mPrevious; - } - - public boolean isNext() { - return mNext != null; - } - - public void setNext(Exercise next) { - mNext = next; - } - - public Exercise getNext() { - return mNext; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bodyweight/fitness/model/LinkedRoutine.java b/app/src/main/java/com/bodyweight/fitness/model/LinkedRoutine.java deleted file mode 100644 index e42b5179..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/LinkedRoutine.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bodyweight.fitness.model; - -import java.io.Serializable; - -public abstract class LinkedRoutine implements Serializable { - public abstract String getTitle(); - public abstract RoutineType getType(); -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/Routine.java b/app/src/main/java/com/bodyweight/fitness/model/Routine.java deleted file mode 100644 index f8519bde..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/Routine.java +++ /dev/null @@ -1,211 +0,0 @@ -package com.bodyweight.fitness.model; - -import com.bodyweight.fitness.model.json.JSONRoutine; -import com.bodyweight.fitness.model.persistence.Glacier; - -import java.io.Serializable; -import java.util.ArrayList; - -public class Routine implements Serializable { - private String mRoutineId; - private String mTitle; - private String mSubtitle; - - private ArrayList mCategories = new ArrayList<>(); - private ArrayList
mSections = new ArrayList<>(); - private ArrayList mExercises = new ArrayList<>(); - private ArrayList mLinkedExercises = new ArrayList<>(); - private ArrayList mLinkedRoutine = new ArrayList<>(); - - public Routine(JSONRoutine JSONRoutine) { - mRoutineId = JSONRoutine.getRoutineId(); - mTitle = JSONRoutine.getTitle(); - mSubtitle = JSONRoutine.getSubtitle(); - - Category currentCategory = null; - Section currentSection = null; - Exercise currentExercise = null; - - for(com.bodyweight.fitness.model.json.JSONLinkedRoutine JSONLinkedRoutine : JSONRoutine.getPartRoutines()) { - if(JSONLinkedRoutine.getType() == RoutineType.CATEGORY) { - currentCategory = new Category( - JSONLinkedRoutine.getCategoryId(), - JSONLinkedRoutine.getTitle() - ); - - mCategories.add(currentCategory); - - /** - * Whether to show categories in the linked routine (e.g. Drawer). - */ - mLinkedRoutine.add(currentCategory); - } - - /** - * Updated sections for each category and add into linked routine. - */ - else if(JSONLinkedRoutine.getType() == RoutineType.SECTION) { - currentSection = new Section( - JSONLinkedRoutine.getSectionId(), - JSONLinkedRoutine.getTitle(), - JSONLinkedRoutine.getDescription(), - JSONLinkedRoutine.getMode() - ); - - currentCategory.insertSection(currentSection); - - mSections.add(currentSection); - mLinkedRoutine.add(currentSection); - } - - /** - * Update exercises for each section, link them together. - */ - else if(JSONLinkedRoutine.getType() == RoutineType.EXERCISE) { - Exercise exercise = new Exercise( - JSONLinkedRoutine.getExerciseId(), - JSONLinkedRoutine.getId(), - JSONLinkedRoutine.getLevel(), - JSONLinkedRoutine.getTitle(), - JSONLinkedRoutine.getDescription(), - JSONLinkedRoutine.getYouTubeId(), - JSONLinkedRoutine.getGifId(), - JSONLinkedRoutine.getGifUrl(), - JSONLinkedRoutine.getDefaultSet() - ); - - /** - * Add exercise into a section. - */ - currentSection.insertExercise(exercise); - - /** - * List contains all exercises, no matter what level. - */ - mExercises.add(exercise); - - /** - * Add only first level of exercise when linking. - */ - if(currentSection.getSectionMode() == SectionMode.LEVELS || currentSection.getSectionMode() == SectionMode.PICK) { - String currentExerciseId = Glacier.get(currentSection.getSectionId(), String.class); - - if(currentExerciseId != null) { - if(exercise.getExerciseId().matches(currentExerciseId)) { - mLinkedExercises.add(exercise); - mLinkedRoutine.add(exercise); - - exercise.setPrevious(currentExercise); - - if(currentExercise != null) { - currentExercise.setNext(exercise); - } - - currentExercise = exercise; - currentSection.setCurrentLevel(exercise); - } - } else { - if(currentSection.getExercises().size() == 1) { - mLinkedExercises.add(exercise); - mLinkedRoutine.add(exercise); - - exercise.setPrevious(currentExercise); - - if(currentExercise != null) { - currentExercise.setNext(exercise); - } - - currentExercise = exercise; - } - } - } else { - exercise.setPrevious(currentExercise); - - if(currentExercise != null) { - currentExercise.setNext(exercise); - } - - currentExercise = exercise; - - mLinkedExercises.add(exercise); - mLinkedRoutine.add(exercise); - } - } - } - } - - public String getRoutineId() { - return mRoutineId; - } - - public void setTitle(String title) { - mTitle = title; - } - - public String getTitle() { - return mTitle; - } - - public void setSubtitle(String subtitle) { - mSubtitle = subtitle; - } - - public String getSubtitle() { - return mSubtitle; - } - - public ArrayList getCategories() { - return mCategories; - } - - public ArrayList
getSections() { - return mSections; - } - - public ArrayList getExercises() { - return mExercises; - } - - public ArrayList getLinkedExercises() { - return mLinkedExercises; - } - - public void setLevel(Exercise exercise, int level) { - Exercise currentSectionExercise = exercise.getSection().getCurrentExercise(); - - if(currentSectionExercise != exercise) { - if(currentSectionExercise.getPrevious() != null) { - currentSectionExercise.getPrevious().setNext(exercise); - } - - if(currentSectionExercise.getNext() != null) { - currentSectionExercise.getNext().setPrevious(exercise); - } - - exercise.setPrevious(currentSectionExercise.getPrevious()); - exercise.setNext(currentSectionExercise.getNext()); - - exercise.getSection().setCurrentLevel(level); - - currentSectionExercise.setPrevious(null); - currentSectionExercise.setNext(null); - - /** - * TODO: When moving models to Kotlin: UNIT TEST IT. - */ - int indexOfCurrentExercise = mLinkedRoutine.indexOf(currentSectionExercise); - if(indexOfCurrentExercise > -1) { - mLinkedRoutine.set(indexOfCurrentExercise, exercise); - } - - int indexOfLinkedExercise = mLinkedExercises.indexOf(currentSectionExercise); - if (indexOfLinkedExercise > -1) { - mLinkedExercises.set(indexOfLinkedExercise, exercise); - } - } - } - - public ArrayList getLinkedRoutine() { - return mLinkedRoutine; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/RoutineType.java b/app/src/main/java/com/bodyweight/fitness/model/RoutineType.java deleted file mode 100644 index 6ba73454..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/RoutineType.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.bodyweight.fitness.model; - -import java.io.Serializable; - -public enum RoutineType implements Serializable { - CATEGORY, - SECTION, - EXERCISE, - EXERCISE_ACTIVE; - - private static RoutineType[] values = RoutineType.values(); - - public static RoutineType fromInt(int value) { - return values[value]; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/Section.java b/app/src/main/java/com/bodyweight/fitness/model/Section.java deleted file mode 100644 index 090e16cc..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/Section.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.bodyweight.fitness.model; - -import java.io.Serializable; -import java.util.ArrayList; - -public class Section extends LinkedRoutine implements Serializable { - private String mSectionId; - private String mTitle; - private String mDescription; - private SectionMode mSectionMode; - - private Category mCategory; - private int mCurrentLevel = 0; - - private ArrayList mExercises = new ArrayList<>(); - - public Section( - String sectionId, - String title, - String description, - SectionMode sectionMode) { - mSectionId = sectionId; - mTitle = title; - mDescription = description; - mSectionMode = sectionMode; - } - - public String getSectionId() { - return mSectionId; - } - - public String getTitle() { - return mTitle; - } - - public String getDescription() { - return mDescription; - } - - public RoutineType getType() { - return RoutineType.SECTION; - } - - public SectionMode getSectionMode() { - return mSectionMode; - } - - public void setCategory(Category category) { - mCategory = category; - } - - public Category getCategory() { - return mCategory; - } - - public void setCurrentLevel(int level) { - if(getSectionMode() == SectionMode.LEVELS || getSectionMode() == SectionMode.PICK) { - if(level < 0 || level >= getExercises().size()) { - return; - } - - mCurrentLevel = level; - } - } - - public void setCurrentLevel(Exercise exercise) { - int current = 0; - for(Exercise ex : getExercises()) { - if(ex.equals(exercise)) { - mCurrentLevel = current; - return; - } - - current++; - } - } - - public int getCurrentLevel() { - return mCurrentLevel; - } - - public int getAvailableLevels() { - if(getSectionMode() == SectionMode.LEVELS || getSectionMode() == SectionMode.PICK) { - return getExercises().size(); - } else { - return 0; - } - } - - public void insertExercise(Exercise exercise) { - exercise.setCategory(mCategory); - exercise.setSection(this); - - mExercises.add(exercise); - } - - public Exercise getCurrentExercise() { - return mExercises.get(mCurrentLevel); - } - - public ArrayList getExercises() { - return mExercises; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bodyweight/fitness/model/SectionMode.java b/app/src/main/java/com/bodyweight/fitness/model/SectionMode.java deleted file mode 100644 index 120f553b..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/SectionMode.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.bodyweight.fitness.model; - -import java.io.Serializable; - -public enum SectionMode implements Serializable { - PICK("pick"), - ALL("all"), - LEVELS("levels"); - - private String mAsString; - - SectionMode(String asString) { - mAsString = asString; - } - - @Override - public String toString() { - return mAsString; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bodyweight/fitness/model/WeightMeasurementUnit.java b/app/src/main/java/com/bodyweight/fitness/model/WeightMeasurementUnit.java deleted file mode 100644 index 5f90cbac..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/WeightMeasurementUnit.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.bodyweight.fitness.model; - -public enum WeightMeasurementUnit { - kg("kg"), - lbs("lbs"); - - private String mAsString; - - WeightMeasurementUnit(String asString) { - mAsString = asString; - } - - public static WeightMeasurementUnit get(String value) { - if (value.equalsIgnoreCase("lbs")) { - return lbs; - } - - return kg; - } - - @Override - public String toString() { - return mAsString; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/json/JSONLinkedRoutine.java b/app/src/main/java/com/bodyweight/fitness/model/json/JSONLinkedRoutine.java deleted file mode 100644 index d76bb6f6..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/json/JSONLinkedRoutine.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.bodyweight.fitness.model.json; - -import com.bodyweight.fitness.model.RoutineType; -import com.bodyweight.fitness.model.SectionMode; -import com.google.gson.annotations.SerializedName; - -public class JSONLinkedRoutine { - @SerializedName("id") - private String mId; - - @SerializedName("categoryId") - private String mCategoryId; - - @SerializedName("sectionId") - private String mSectionId; - - @SerializedName("exerciseId") - private String mExerciseId; - - @SerializedName("level") - private String mLevel; - - @SerializedName("title") - private String mTitle; - - @SerializedName("description") - private String mDescription; - - @SerializedName("youTubeId") - private String mYouTubeId; - - @SerializedName("gifId") - private String mGifId; - - @SerializedName("gifUrl") - private String mGifUrl; - - @SerializedName("type") - private String mType; - - /** - * Mode can be one of those: All, Pick, Levels - */ - @SerializedName("mode") - private String mMode; - - @SerializedName("defaultSet") - private String mDefaultSet; - - public String getId() { - return mId; - } - - public String getCategoryId() { - return mCategoryId; - } - - public String getSectionId() { - return mSectionId; - } - - public String getExerciseId() { - return mExerciseId; - } - - public String getLevel() { - return mLevel; - } - - public String getTitle() { - return mTitle; - } - - public String getDescription() { - return mDescription; - } - - public String getYouTubeId() { - return mYouTubeId; - } - - public String getGifId() { - return mGifId; - } - - public String getGifUrl() { - return mGifUrl; - } - - public RoutineType getType() { - if(mType.matches("category")) { - return RoutineType.CATEGORY; - } else if(mType.matches("section")) { - return RoutineType.SECTION; - } else { - return RoutineType.EXERCISE; - } - } - - public SectionMode getMode() { - if(mMode.matches("all")) { - return SectionMode.ALL; - } else if(mMode.matches("pick")) { - return SectionMode.PICK; - } else { - return SectionMode.LEVELS; - } - } - - public String getDefaultSet() { - return mDefaultSet; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bodyweight/fitness/model/json/JSONRoutine.java b/app/src/main/java/com/bodyweight/fitness/model/json/JSONRoutine.java deleted file mode 100644 index ecd5ff61..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/json/JSONRoutine.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.bodyweight.fitness.model.json; - -import com.google.gson.annotations.SerializedName; - -import java.util.ArrayList; - -public class JSONRoutine { - @SerializedName("routineId") - private String mRoutineId; - - @SerializedName("title") - private String mTitle; - - @SerializedName("subtitle") - private String mSubtitle; - - @SerializedName("routine") - private ArrayList mJSONLinkedRoutines = new ArrayList<>(); - - public String getRoutineId() { - return mRoutineId; - } - - public String getTitle() { - return mTitle; - } - - public String getSubtitle() { - return mSubtitle; - } - - public ArrayList getPartRoutines() { - return mJSONLinkedRoutines; - } - - public int getSize() { - if(mJSONLinkedRoutines != null) { - return mJSONLinkedRoutines.size(); - } - - return 0; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryCategory.java b/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryCategory.java deleted file mode 100644 index 302150f2..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryCategory.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.bodyweight.fitness.model.repository; - -import io.realm.RealmList; -import io.realm.RealmObject; -import io.realm.annotations.Index; -import io.realm.annotations.PrimaryKey; -import io.realm.annotations.Required; - -public class RepositoryCategory extends RealmObject { - @PrimaryKey @Required - private String id; - - @Index - private String categoryId; - - private String title; - - private RepositoryRoutine routine; - - private RealmList sections = new RealmList<>(); - private RealmList exercises = new RealmList<>(); - - public void setId(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setCategoryId(String categoryId) { - this.categoryId = categoryId; - } - - public String getCategoryId() { - return categoryId; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getTitle() { - return title; - } - - public void setRoutine(RepositoryRoutine routine) { - this.routine = routine; - } - - public RepositoryRoutine getRoutine() { - return routine; - } - - public void setSections(RealmList sections) { - this.sections = sections; - } - - public RealmList getSections() { - return sections; - } - - public void setExercises(RealmList exercises) { - this.exercises = exercises; - } - - public RealmList getExercises() { - return exercises; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryExercise.java b/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryExercise.java deleted file mode 100644 index c0971a3b..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryExercise.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.bodyweight.fitness.model.repository; - -import io.realm.RealmList; -import io.realm.RealmObject; -import io.realm.annotations.Index; -import io.realm.annotations.PrimaryKey; -import io.realm.annotations.Required; - -public class RepositoryExercise extends RealmObject { - @PrimaryKey @Required - private String id; - - @Index - private String exerciseId; - - private String title; - private String description; - private String defaultSet; - - private boolean visible = false; - - private RepositoryRoutine routine; - private RepositoryCategory category; - private RepositorySection section; - - private RealmList sets = new RealmList<>(); - - public void setId(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setExerciseId(String exerciseId) { - this.exerciseId = exerciseId; - } - - public String getExerciseId() { - return exerciseId; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getTitle() { - return title; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getDescription() { - return description; - } - - public void setDefaultSet(String defaultSet) { - this.defaultSet = defaultSet; - } - - public String getDefaultSet() { - return defaultSet; - } - - public void setVisible(boolean visible) { - this.visible = visible; - } - - public boolean isVisible() { - return visible; - } - - public void setRoutine(RepositoryRoutine routine) { - this.routine = routine; - } - - public RepositoryRoutine getRoutine() { - return routine; - } - - public void setCategory(RepositoryCategory category) { - this.category = category; - } - - public RepositoryCategory getCategory() { - return category; - } - - public void setSection(RepositorySection section) { - this.section = section; - } - - public RepositorySection getSection() { - return section; - } - - public void setSets(RealmList sets) { - this.sets = sets; - } - - public RealmList getSets() { - return this.sets; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryRoutine.java b/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryRoutine.java deleted file mode 100644 index f7cad28e..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositoryRoutine.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.bodyweight.fitness.model.repository; - -import java.util.Date; - -import io.realm.RealmList; -import io.realm.RealmObject; -import io.realm.annotations.Index; -import io.realm.annotations.PrimaryKey; -import io.realm.annotations.Required; - -public class RepositoryRoutine extends RealmObject { - @PrimaryKey @Required - private String id; - - @Index - private String routineId; - - private String title; - private String subtitle; - - @Index - private Date startTime; - - @Index - private Date lastUpdatedTime; - - private RealmList categories = new RealmList<>(); - private RealmList sections = new RealmList<>(); - private RealmList exercises = new RealmList<>(); - - public void setId(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setRoutineId(String routineId) { - this.routineId = routineId; - } - - public String getRoutineId() { - return routineId; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getTitle() { - return title; - } - - public void setSubtitle(String subtitle) { - this.subtitle = subtitle; - } - - public String getSubtitle() { - return subtitle; - } - - public void setStartTime(Date startTime) { - this.startTime = startTime; - } - - public Date getStartTime() { - return startTime; - } - - public void setLastUpdatedTime(Date lastUpdatedTime) { - this.lastUpdatedTime = lastUpdatedTime; - } - - public Date getLastUpdatedTime() { - return lastUpdatedTime; - } - - public void setCategories(RealmList categories) { - this.categories = categories; - } - - public RealmList getCategories() { - return categories; - } - - public void setSections(RealmList sections) { - this.sections = sections; - } - - public RealmList getSections() { - return sections; - } - - public void setExercises(RealmList exercises) { - this.exercises = exercises; - } - - public RealmList getExercises() { - return exercises; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositorySection.java b/app/src/main/java/com/bodyweight/fitness/model/repository/RepositorySection.java deleted file mode 100644 index 60aad2a9..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositorySection.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.bodyweight.fitness.model.repository; - -import io.realm.RealmList; -import io.realm.RealmObject; -import io.realm.annotations.Index; -import io.realm.annotations.PrimaryKey; -import io.realm.annotations.Required; - -public class RepositorySection extends RealmObject { - @PrimaryKey @Required - private String id; - - @Index - private String sectionId; - - private String title; - private String mode; - - private RepositoryRoutine routine; - private RepositoryCategory category; - - private RealmList exercises = new RealmList<>(); - - public void setId(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setSectionId(String sectionId) { - this.sectionId = sectionId; - } - - public String getSectionId() { - return sectionId; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getTitle() { - return title; - } - - public void setMode(String mode) { - this.mode = mode; - } - - public String getMode() { - return mode; - } - - public void setRoutine(RepositoryRoutine routine) { - this.routine = routine; - } - - public RepositoryRoutine getRoutine() { - return routine; - } - - public void setCategory(RepositoryCategory category) { - this.category = category; - } - - public RepositoryCategory getCategory() { - return category; - } - - public void setExercises(RealmList exercises) { - this.exercises = exercises; - } - - public RealmList getExercises() { - return exercises; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositorySet.java b/app/src/main/java/com/bodyweight/fitness/model/repository/RepositorySet.java deleted file mode 100644 index b33306d1..00000000 --- a/app/src/main/java/com/bodyweight/fitness/model/repository/RepositorySet.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.bodyweight.fitness.model.repository; - -import io.realm.RealmObject; -import io.realm.annotations.PrimaryKey; -import io.realm.annotations.Required; - -public class RepositorySet extends RealmObject { - @PrimaryKey @Required - private String id; - - private boolean isTimed = false; - - private double weight = 0; - private int reps = 0; - private int seconds = 0; - - private RepositoryExercise exercise; - - public void setId(String id) { - this.id = id; - } - - public String getId() { - return this.id; - } - - public void setIsTimed(boolean isTimed) { - this.isTimed = isTimed; - } - - public boolean isTimed() { - return this.isTimed; - } - - public void setWeight(double weight) { - this.weight = weight; - } - - public double getWeight() { - return this.weight; - } - - public void setReps(int reps) { - this.reps = reps; - } - - public int getReps() { - return this.reps; - } - - public void setSeconds(int seconds) { - this.seconds = seconds; - } - - public int getSeconds() { - return this.seconds; - } - - public void setExercise(RepositoryExercise exercise) { - this.exercise = exercise; - } - - public RepositoryExercise getExercise() { - return exercise; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/model/persistence/Duration.java b/app/src/main/java/com/bodyweight/fitness/persistence/Duration.java similarity index 90% rename from app/src/main/java/com/bodyweight/fitness/model/persistence/Duration.java rename to app/src/main/java/com/bodyweight/fitness/persistence/Duration.java index 3c2d6e9f..879be5ee 100644 --- a/app/src/main/java/com/bodyweight/fitness/model/persistence/Duration.java +++ b/app/src/main/java/com/bodyweight/fitness/persistence/Duration.java @@ -1,4 +1,4 @@ -package com.bodyweight.fitness.model.persistence; +package com.bodyweight.fitness.persistence; public interface Duration { /** diff --git a/app/src/main/java/com/bodyweight/fitness/model/persistence/Glacier.java b/app/src/main/java/com/bodyweight/fitness/persistence/Glacier.java similarity index 96% rename from app/src/main/java/com/bodyweight/fitness/model/persistence/Glacier.java rename to app/src/main/java/com/bodyweight/fitness/persistence/Glacier.java index f8674169..f1faf9b6 100644 --- a/app/src/main/java/com/bodyweight/fitness/model/persistence/Glacier.java +++ b/app/src/main/java/com/bodyweight/fitness/persistence/Glacier.java @@ -1,10 +1,10 @@ -package com.bodyweight.fitness.model.persistence; +package com.bodyweight.fitness.persistence; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.bodyweight.fitness.model.persistence.file.FileObjectPersister; +import com.bodyweight.fitness.persistence.file.FileObjectPersister; import java.io.File; import java.util.HashMap; @@ -19,7 +19,7 @@ public interface Callback { private static FileObjectPersister mFileObjectPersister; - private static HashMap hashMap = new HashMap<>(); + private static HashMap hashMap = new HashMap(); public synchronized static void init() { mFileObjectPersister = new FileObjectPersister(); diff --git a/app/src/main/java/com/bodyweight/fitness/model/persistence/file/FileObjectPersister.java b/app/src/main/java/com/bodyweight/fitness/persistence/file/FileObjectPersister.java similarity index 94% rename from app/src/main/java/com/bodyweight/fitness/model/persistence/file/FileObjectPersister.java rename to app/src/main/java/com/bodyweight/fitness/persistence/file/FileObjectPersister.java index 31baf857..3153e2b4 100644 --- a/app/src/main/java/com/bodyweight/fitness/model/persistence/file/FileObjectPersister.java +++ b/app/src/main/java/com/bodyweight/fitness/persistence/file/FileObjectPersister.java @@ -1,10 +1,10 @@ -package com.bodyweight.fitness.model.persistence.file; +package com.bodyweight.fitness.persistence.file; import android.content.Context; import android.support.annotation.NonNull; -import com.bodyweight.fitness.model.exception.CacheDirectoryCreationException; -import com.bodyweight.fitness.model.persistence.Duration; +import com.bodyweight.fitness.exception.CacheDirectoryCreationException; +import com.bodyweight.fitness.persistence.Duration; import org.apache.commons.io.FileUtils; diff --git a/app/src/main/java/com/bodyweight/fitness/stream/RepositoryStream.java b/app/src/main/java/com/bodyweight/fitness/stream/RepositoryStream.java deleted file mode 100644 index d230589a..00000000 --- a/app/src/main/java/com/bodyweight/fitness/stream/RepositoryStream.java +++ /dev/null @@ -1,211 +0,0 @@ -package com.bodyweight.fitness.stream; - -import org.joda.time.DateTime; - -import java.util.Date; -import java.util.UUID; - -import com.bodyweight.fitness.App; -import com.bodyweight.fitness.model.Exercise; -import com.bodyweight.fitness.model.Routine; -import com.bodyweight.fitness.model.SectionMode; -import com.bodyweight.fitness.model.repository.RepositoryCategory; -import com.bodyweight.fitness.model.repository.RepositoryExercise; -import com.bodyweight.fitness.model.repository.RepositoryRoutine; -import com.bodyweight.fitness.model.repository.RepositorySection; -import com.bodyweight.fitness.model.repository.RepositorySet; - -import io.realm.DynamicRealm; -import io.realm.DynamicRealmObject; -import io.realm.Realm; -import io.realm.RealmConfiguration; - -import io.realm.RealmObjectSchema; -import io.realm.RealmSchema; - -public class RepositoryStream { - private static RepositoryStream sRepositoryStream = null; - - public static RepositoryStream getInstance() { - if(sRepositoryStream == null) { - sRepositoryStream = new RepositoryStream(); - } - - return sRepositoryStream; - } - - private RepositoryStream() {} - - public Realm getRealm() { - return Realm.getInstance(new RealmConfiguration.Builder(App.getContext()) - .name("bodyweight.fitness.realm") - .schemaVersion(2) - .migration((DynamicRealm realm, long oldVersion, long newVersion) -> { - RealmSchema schema = realm.getSchema(); - RealmObjectSchema routineSchema = schema.get("RepositoryRoutine"); - - if (oldVersion == 1) { - routineSchema - .addField("title", String.class) - .addField("subtitle", String.class) - .transform((DynamicRealmObject obj) -> { - obj.set("title", "Bodyweight Fitness"); - obj.set("subtitle", "Recommended Routine"); - }); - } - }) - .build()); - } - - public RepositoryRoutine buildRealmRoutine(Routine routine) { - Realm realm = getRealm(); - - // TODO: Use Kotlin Realm Transaction - realm.beginTransaction(); - - RepositoryRoutine repositoryRoutine = realm.createObject(RepositoryRoutine.class); - repositoryRoutine.setId("Routine-" + UUID.randomUUID().toString()); - repositoryRoutine.setRoutineId(routine.getRoutineId()); - repositoryRoutine.setTitle(routine.getTitle()); - repositoryRoutine.setSubtitle(routine.getSubtitle()); - repositoryRoutine.setStartTime(new DateTime().toDate()); - repositoryRoutine.setLastUpdatedTime(new DateTime().toDate()); - - RepositoryCategory repositoryCategory = null; - RepositorySection repositorySection = null; - - for(Exercise exercise : routine.getExercises()) { - RepositoryExercise repositoryExercise = realm.createObject(RepositoryExercise.class); - repositoryExercise.setId("Exercise-" + UUID.randomUUID().toString()); - repositoryExercise.setExerciseId(exercise.getExerciseId()); - repositoryExercise.setTitle(exercise.getTitle()); - repositoryExercise.setDescription(exercise.getDescription()); - repositoryExercise.setDefaultSet(exercise.getDefaultSet()); - - RepositorySet repositorySet = realm.createObject(RepositorySet.class); - repositorySet.setId("Set-" + UUID.randomUUID().toString()); - - if (exercise.getDefaultSet().equals("weighted")) { - repositorySet.setIsTimed(false); - } else { - repositorySet.setIsTimed(true); - } - - repositorySet.setSeconds(0); - repositorySet.setWeight(0); - repositorySet.setReps(0); - repositorySet.setExercise(repositoryExercise); - - repositoryExercise.getSets().add(repositorySet); - - if(repositoryCategory == null || !repositoryCategory.getTitle().equalsIgnoreCase(exercise.getCategory().getTitle())) { - repositoryCategory = realm.createObject(RepositoryCategory.class); - repositoryCategory.setId("Category-" + UUID.randomUUID().toString()); - repositoryCategory.setCategoryId(exercise.getCategory().getCategoryId()); - repositoryCategory.setTitle(exercise.getCategory().getTitle()); - repositoryCategory.setRoutine(repositoryRoutine); - - repositoryRoutine.getCategories().add(repositoryCategory); - } - - if(repositorySection == null || !repositorySection.getTitle().equalsIgnoreCase(exercise.getSection().getTitle())) { - repositorySection = realm.createObject(RepositorySection.class); - repositorySection.setId("Section-" + UUID.randomUUID().toString()); - repositorySection.setSectionId(exercise.getSection().getSectionId()); - repositorySection.setTitle(exercise.getSection().getTitle()); - repositorySection.setMode(exercise.getSection().getSectionMode().toString()); - repositorySection.setRoutine(repositoryRoutine); - repositorySection.setCategory(repositoryCategory); - - repositoryRoutine.getSections().add(repositorySection); - repositoryCategory.getSections().add(repositorySection); - } - - repositoryExercise.setRoutine(repositoryRoutine); - repositoryExercise.setCategory(repositoryCategory); - repositoryExercise.setSection(repositorySection); - - /** - * Hide exercises not relevant to user level. - */ - if(exercise.getSection().getSectionMode().equals(SectionMode.LEVELS) || exercise.getSection().getSectionMode().equals(SectionMode.PICK)) { - if(exercise.equals(exercise.getSection().getCurrentExercise())) { - repositoryExercise.setVisible(true); - } else { - repositoryExercise.setVisible(false); - } - } else { - repositoryExercise.setVisible(true); - } - - repositoryRoutine.getExercises().add(repositoryExercise); - repositoryCategory.getExercises().add(repositoryExercise); - repositorySection.getExercises().add(repositoryExercise); - } - - realm.commitTransaction(); - - return repositoryRoutine; - } - - public RepositoryRoutine getRepositoryRoutineForPrimaryKeyRoutineId(String primaryKeyRoutineId) { - return getRealm() - .where(RepositoryRoutine.class) - .equalTo("id", primaryKeyRoutineId) - .findFirst(); - } - - public RepositoryRoutine getRepositoryRoutineForToday() { - final Date start = new DateTime() - .withTimeAtStartOfDay() - .toDate(); - - final Date end = new DateTime() - .withTimeAtStartOfDay() - .plusDays(1) - .minusSeconds(1) - .toDate(); - - String routineId = RoutineStream.getInstance().getRoutine().getRoutineId(); - - RepositoryRoutine repositoryRoutine = getRealm() - .where(RepositoryRoutine.class) - .between("startTime", start, end) - .equalTo("routineId", routineId) - .findFirst(); - - if (repositoryRoutine == null) { - repositoryRoutine = buildRealmRoutine( - RoutineStream.getInstance().getRoutine() - ); - } - - return repositoryRoutine; - } - - public boolean repositoryRoutineForTodayExists() { - final Date start = new DateTime() - .withTimeAtStartOfDay() - .toDate(); - - final Date end = new DateTime() - .withTimeAtStartOfDay() - .plusDays(1) - .minusSeconds(1) - .toDate(); - - String routineId = RoutineStream.getInstance().getRoutine().getRoutineId(); - - RepositoryRoutine repositoryRoutine = getRealm() - .where(RepositoryRoutine.class) - .between("startTime", start, end) - .equalTo("routineId", routineId) - .findFirst(); - - if (repositoryRoutine == null) { - return false; - } - - return true; - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/stream/RoutineStream.java b/app/src/main/java/com/bodyweight/fitness/stream/RoutineStream.java deleted file mode 100644 index a66fef85..00000000 --- a/app/src/main/java/com/bodyweight/fitness/stream/RoutineStream.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.bodyweight.fitness.stream; - -import com.bodyweight.fitness.App; -import com.bodyweight.fitness.model.Exercise; -import com.bodyweight.fitness.model.json.JSONRoutine; -import com.bodyweight.fitness.model.persistence.Glacier; -import com.bodyweight.fitness.utils.PreferenceUtils; -import com.google.gson.Gson; - -import org.apache.commons.io.IOUtils; - -import java.io.IOException; - -import com.bodyweight.fitness.R; -import com.bodyweight.fitness.model.Routine; - -import rx.Observable; -import rx.android.schedulers.AndroidSchedulers; -import rx.subjects.PublishSubject; - -public class RoutineStream { - private static RoutineStream sInstance; - - private Routine mRoutine; - private Exercise mExercise; - - private final PublishSubject mRoutineSubject = PublishSubject.create(); - private final PublishSubject mRoutineChangedSubject = PublishSubject.create(); - private final PublishSubject mExerciseSubject = PublishSubject.create(); - private final PublishSubject mLevelChangedSubject = PublishSubject.create(); - - private RoutineStream() { - setRoutine(R.raw.bodyweight_fitness_recommended_routine); - } - - public static RoutineStream getInstance() { - if(sInstance == null) { - sInstance = new RoutineStream(); - } - - return sInstance; - } - - public void setRoutine(Routine routine) { - mRoutine = routine; - - PreferenceUtils.getInstance().setDefaultRoutine(mRoutine.getRoutineId()); - - mExercise = mRoutine.getLinkedExercises().get(0); - - mRoutineSubject.onNext(mRoutine); - mExerciseSubject.onNext(mExercise); - - mRoutineChangedSubject.onNext(mRoutine); - } - - private void setRoutine(int resource) { - mRoutine = getRoutine(resource); - - setRoutine(mRoutine); - } - - public Routine getRoutine() { - return mRoutine; - } - - public Routine getRoutine(int resource) { - try { - JSONRoutine jsonRoutine = new Gson().fromJson(IOUtils.toString(App.getContext() - .getResources() - .openRawResource(resource) - ), JSONRoutine.class); - - return new Routine(jsonRoutine); - } catch(IOException e) {} - - return null; - } - - public void setExercise(Exercise exercise) { - mExercise = exercise; - mExerciseSubject.onNext(exercise); - } - - public Exercise getExercise() { - return mExercise; - } - - public void setLevel(Exercise exercise, int level) { - mRoutine.setLevel(exercise, level); - - setExercise(exercise); - - // We save our current exercise for given section - Glacier.put( - exercise.getSection().getSectionId(), - exercise.getSection().getCurrentExercise().getExerciseId() - ); - - mLevelChangedSubject.onNext(mRoutine); - } - - public Observable getRoutineObservable() { - - Observable routineObservable = Observable - .just(mRoutine) - .observeOn(AndroidSchedulers.mainThread()) - .publish() - .refCount(); - - return Observable.merge(mRoutineSubject, routineObservable); - } - - public Observable getRoutineChangedObservable() { - return mRoutineChangedSubject; - } - - public Observable getExerciseObservable() { - Observable exerciseObservable = Observable - .just(mExercise) - .observeOn(AndroidSchedulers.mainThread()) - .publish() - .refCount(); - - return Observable.merge(mExerciseSubject, exerciseObservable); - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/utils/ApplicationStoreUtils.java b/app/src/main/java/com/bodyweight/fitness/utils/ApplicationStoreUtils.java deleted file mode 100644 index 26a75b97..00000000 --- a/app/src/main/java/com/bodyweight/fitness/utils/ApplicationStoreUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.bodyweight.fitness.utils; - -public class ApplicationStoreUtils { - private static final String androidInstallerPackage = "com.android.vending"; - private static final String amazonInstallerPackage = "com.amazon.venezia"; - - public enum ApplicationStore { - GooglePlay("https://play.google.com/store/apps/details?id=com.bodyweight.fitness.pro"), - AmazonStore("amzn://apps/android?p=io.mazur.fit.pro"); - - private String mApplicationStoreAppUrl; - - ApplicationStore(String applicationStoreAppUrl) { - mApplicationStoreAppUrl = applicationStoreAppUrl; - } - - public String getApplicationStoreAppUrl() { - return mApplicationStoreAppUrl; - } - } - - public static ApplicationStore getApplicationStoreUrl(String installerPackageName) { - if (installerPackageName != null) { - if (installerPackageName.equals(amazonInstallerPackage)) { - return ApplicationStore.AmazonStore; - } else if (installerPackageName.equals(androidInstallerPackage)) { - return ApplicationStore.GooglePlay; - } - } else if (android.os.Build.MANUFACTURER.equals("Amazon")) { - return ApplicationStore.AmazonStore; - } - - return ApplicationStore.GooglePlay; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bodyweight/fitness/utils/PreferenceUtils.java b/app/src/main/java/com/bodyweight/fitness/utils/PreferenceUtils.java deleted file mode 100644 index 79ae51b6..00000000 --- a/app/src/main/java/com/bodyweight/fitness/utils/PreferenceUtils.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.bodyweight.fitness.utils; - -import android.preference.PreferenceManager; - -import com.bodyweight.fitness.App; -import com.bodyweight.fitness.Constants; -import com.bodyweight.fitness.model.WeightMeasurementUnit; - -import com.bodyweight.fitness.R; - -public class PreferenceUtils { - private static class InstanceHolder { - private static final PreferenceUtils mInstance = new PreferenceUtils(); - } - - public static PreferenceUtils getInstance() { - return InstanceHolder.mInstance; - } - - private PreferenceUtils() { - PreferenceManager.setDefaultValues(App.getContext(), R.xml.settings, false); - } - - public void setDefaultRoutine(String defaultRoutine) { - PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .edit() - .putString(Constants.INSTANCE.getPREFERENCE_DEFAULT_ROUTINE(), defaultRoutine) - .commit(); - } - - public String getDefaultRoutine() { - return PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getString(Constants.INSTANCE.getPREFERENCE_DEFAULT_ROUTINE(), "routine0"); - } - - public WeightMeasurementUnit getWeightMeasurementUnit() { - String value = PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getString(Constants.INSTANCE.getPREFERENCE_WEIGHT_MEASUREMENT_UNITS(), "kg"); - - return WeightMeasurementUnit.get(value); - } - - public boolean playSoundWhenTimerStops() { - return PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getBoolean(Constants.INSTANCE.getPREFERENCE_PLAY_SOUND_WHEN_TIMER_STOPS(), true); - } - - public boolean automaticallyLogWorkoutTime() { - return PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getBoolean(Constants.INSTANCE.getPREFERENCE_AUTOMATICALLY_LOG_WORKOUT_TIME(), true); - } - - public boolean keepScreenOnWhenAppIsRunning() { - return PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getBoolean(Constants.INSTANCE.getPREFERENCE_KEEP_SCREEN_ON(), true); - } - - public void setTimerValue(String exerciseId, long value) { - PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .edit() - .putLong(String.format("%s%s", Constants.INSTANCE.getPREFERENCE_TIMER_KEY(), exerciseId), value) - .commit(); - } - - public void setNumberOfReps(String exerciseId, int value) { - PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .edit() - .putInt(String.format("%s%s", Constants.INSTANCE.getPREFERENCE_NUMBER_OF_REPS_KEY(), exerciseId), value) - .commit(); - } - - public long getTimerValueForExercise(String exerciseId, long defaultValue) { - return PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getLong(String.format("%s%s", Constants.INSTANCE.getPREFERENCE_TIMER_KEY(), exerciseId), defaultValue); - } - - public int getNumberOfRepsForExercise(String exerciseId, int defaultValue) { - return PreferenceManager.getDefaultSharedPreferences(App.getContext()) - .getInt(String.format("%s%s", Constants.INSTANCE.getPREFERENCE_NUMBER_OF_REPS_KEY(), exerciseId), defaultValue); - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/view/dialog/LogWorkoutDialog.java b/app/src/main/java/com/bodyweight/fitness/view/dialog/LogWorkoutDialog.java deleted file mode 100644 index ff540b72..00000000 --- a/app/src/main/java/com/bodyweight/fitness/view/dialog/LogWorkoutDialog.java +++ /dev/null @@ -1,804 +0,0 @@ -package com.bodyweight.fitness.view.dialog; - -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.support.v7.widget.AppCompatImageButton; -import android.support.v7.widget.Toolbar; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.View; -import android.view.Window; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.bodyweight.fitness.Constants; -import com.bodyweight.fitness.model.Exercise; -import com.bodyweight.fitness.model.SectionMode; -import com.bodyweight.fitness.model.WeightMeasurementUnit; -import com.bodyweight.fitness.model.repository.RepositoryExercise; -import com.bodyweight.fitness.model.repository.RepositorySet; -import com.bodyweight.fitness.stream.RepositoryStream; - -import org.joda.time.DateTime; -import org.joda.time.Duration; - -import java.util.ArrayList; -import java.util.UUID; - -import butterknife.ButterKnife; -import butterknife.InjectView; - -import com.bodyweight.fitness.R; -import com.bodyweight.fitness.model.repository.RepositoryRoutine; -import com.bodyweight.fitness.stream.RoutineStream; -import com.bodyweight.fitness.stream.Stream; -import com.bodyweight.fitness.utils.PreferenceUtils; -import com.bodyweight.fitness.view.listener.RepeatListener; - -import io.realm.Realm; - -public class LogWorkoutDialog extends DialogFragment { - private static final int REPEAT_INITIAL_INTERVAL = 400; - private static final int REPEAT_NORMAL_INTERVAL = 100; - - @InjectView(R.id.toolbar) - Toolbar mToolbar; - - @InjectView(R.id.saveButton) - Button mSaveButton; - - @InjectView(R.id.setView) - LinearLayout mSetView; - - @InjectView(R.id.actionView) - View mActionView; - - @InjectView(R.id.setValue) - TextView mActionViewSetValue; - - @InjectView(R.id.weightValue) - TextView mActionViewWeightValue; - - @InjectView(R.id.weightDescription) - TextView mActionViewWeightDescription; - - @InjectView(R.id.repsValue) - TextView mActionViewRepsValue; - - @InjectView(R.id.repsDescription) - TextView mActionViewRepsDescription; - - @InjectView(R.id.weightIncrease) - AppCompatImageButton mWeightIncrease; - - @InjectView(R.id.weightDecrease) - AppCompatImageButton mWeightDecrease; - - @InjectView(R.id.repsIncrease) - AppCompatImageButton mRepsIncrease; - - @InjectView(R.id.repsDecrease) - AppCompatImageButton mRepsDecrease; - - private LinearLayout mRowLayout; - - private ArrayList mViewSets = new ArrayList<>(); - - private RepositoryRoutine mRepositoryRoutine; - private RepositoryExercise mRepositoryExercise; - private RepositorySet mSet; - - private WeightMeasurementUnit mWeightMeasurementUnit; - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - String primaryKeyRoutineId = getArguments().getString(Constants.INSTANCE.getPrimaryKeyRoutineId()); - String exerciseId = getArguments().getString(Constants.INSTANCE.getExerciseId()); - - /** - * routineId = pass id in order to show dialog for different date. - */ - if (primaryKeyRoutineId == null) { - mRepositoryRoutine = RepositoryStream.getInstance().getRepositoryRoutineForToday(); - - for (RepositoryExercise repositoryExercise : mRepositoryRoutine.getExercises()) { - if (repositoryExercise.getExerciseId().equals(exerciseId)) { - mRepositoryExercise = repositoryExercise; - - break; - } - } - } else { - mRepositoryRoutine = RepositoryStream.getInstance().getRepositoryRoutineForPrimaryKeyRoutineId(primaryKeyRoutineId); - - for (RepositoryExercise repositoryExercise : mRepositoryRoutine.getExercises()) { - if (repositoryExercise.getExerciseId().equals(exerciseId)) { - mRepositoryExercise = repositoryExercise; - - break; - } - } - } - - Dialog dialog = new Dialog(getContext()); - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - dialog.setContentView(R.layout.view_dialog_log_workout); - dialog.setCanceledOnTouchOutside(true); - - ButterKnife.inject(this, dialog); - - mWeightIncrease.setOnTouchListener( - new RepeatListener(REPEAT_INITIAL_INTERVAL, REPEAT_NORMAL_INTERVAL, (view) -> onClickIncreaseWeight()) - ); - - mWeightDecrease.setOnTouchListener( - new RepeatListener(REPEAT_INITIAL_INTERVAL, REPEAT_NORMAL_INTERVAL, (view) -> onClickDecreaseWeight()) - ); - - mRepsIncrease.setOnTouchListener( - new RepeatListener(REPEAT_INITIAL_INTERVAL, REPEAT_NORMAL_INTERVAL, (view) -> onClickIncreaseReps()) - ); - - mRepsDecrease.setOnTouchListener( - new RepeatListener(REPEAT_INITIAL_INTERVAL, REPEAT_NORMAL_INTERVAL, (view) -> onClickDecreaseReps()) - ); - - mWeightMeasurementUnit = PreferenceUtils - .getInstance() - .getWeightMeasurementUnit(); - - mActionView.setVisibility(View.GONE); - - mToolbar.setTitle(mRepositoryExercise.getTitle()); - mToolbar.setSubtitle(mRepositoryExercise.getDescription()); - - inflateToolbarMenu(); - - buildSets(); - - mSaveButton.setOnClickListener(v -> dialog.dismiss()); - - return dialog; - } - - @Override - public void onDismiss(DialogInterface dialog) { - super.onDismiss(dialog); - - String mode = mRepositoryExercise.getSection().getMode(); - - if (mode.equals(SectionMode.LEVELS.toString()) || mode.equals(SectionMode.PICK.toString())) { - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if (isCompleted(mRepositoryExercise)) { - mRepositoryExercise.setVisible(true); - } else { - mRepositoryExercise.setVisible(false); - } - - realm.commitTransaction(); - } - - Stream.INSTANCE.setRepository(); - } - - public void buildSets() { - for (RepositorySet set : mRepositoryExercise.getSets()) { - if (shouldAddSet()) { - if (set.isTimed()) { - addTimedSet(set); - } else { - addSet(set); - } - } - } - - updateToolbarMenu(); - } - - public void actionViewOpened() { - invalidateToolbarMenu(); - - mSaveButton.setText("Back"); - mSaveButton.setOnClickListener(v -> { - updateSets(); - - mSetView.setVisibility(View.VISIBLE); - mActionView.setVisibility(View.GONE); - - actionViewClosed(); - }); - } - - public void actionViewClosed() { - inflateToolbarMenu(); - - mSaveButton.setText("Save"); - mSaveButton.setOnClickListener(v -> { - this.dismiss(); - }); - } - - public void updateSets() { - int i = 0; - for(View view : mViewSets) { - RepositorySet set = mRepositoryExercise.getSets().get(i); - - if(set.isTimed()) { - updateTimedSet(set, view); - } else { - updateSet(set, view); - } - - i++; - } - } - - public boolean shouldAddSet() { - int numberOfSets = mViewSets.size(); - - if(numberOfSets >= Constants.INSTANCE.getMAXIMUM_NUMBER_OF_SETS()) { - return false; - } - - return true; - } - - public boolean shouldRemoveSet() { - int numberOfSets = mViewSets.size(); - - if(numberOfSets == 1) { - return false; - } - - return true; - } - - public void addRow() { - int numberOfSets = mViewSets.size(); - - if(numberOfSets == 0 || numberOfSets == 3 || numberOfSets == 6 || numberOfSets == 9) { - View view = LayoutInflater.from(getContext()) - .inflate(R.layout.view_dialog_log_workout_row, mSetView, false); - - mRowLayout = (LinearLayout) view; - mSetView.addView(view); - } - } - - public boolean addSet(RepositorySet set) { - addRow(); - - View view = LayoutInflater.from(getContext()) - .inflate(R.layout.view_dialog_log_workout_set, mSetView, false); - - updateSet(set, view); - - view.setOnClickListener(v -> { - updateActionView(set, mRepositoryExercise.getSets().indexOf(set) + 1); - - mSetView.setVisibility(View.GONE); - mActionView.setVisibility(View.VISIBLE); - - actionViewOpened(); - }); - - mRowLayout.addView(view); - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if(!mRepositoryExercise.getSets().contains(set)) { - mRepositoryExercise.getSets().add(set); - } - - realm.commitTransaction(); - - mViewSets.add(view); - - setLastUpdatedTime(); - - return true; - } - - public boolean addTimedSet(RepositorySet set) { - addRow(); - - View view = LayoutInflater.from(getContext()) - .inflate(R.layout.view_dialog_log_workout_timed_set, mSetView, false); - - updateTimedSet(set, view); - - view.setOnClickListener(v -> { - updateActionViewForTimedSet(set, mRepositoryExercise.getSets().indexOf(set) + 1); - - mSetView.setVisibility(View.GONE); - mActionView.setVisibility(View.VISIBLE); - - actionViewOpened(); - }); - - mRowLayout.addView(view); - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if(!mRepositoryExercise.getSets().contains(set)) { - mRepositoryExercise.getSets().add(set); - } - - realm.commitTransaction(); - - setLastUpdatedTime(); - - mViewSets.add(view); - - return true; - } - - public boolean removeLastSet() { - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - mRepositoryExercise.getSets().remove(mRepositoryExercise.getSets().size() - 1); - - realm.commitTransaction(); - - mViewSets.remove(mViewSets.size() - 1); - - mRowLayout.removeViewAt(mRowLayout.getChildCount() - 1); - - if (mRowLayout.getChildCount() == 0) { - mSetView.removeView(mRowLayout); - - mRowLayout = (LinearLayout) mSetView.getChildAt(mSetView.getChildCount() - 1); - } - - return true; - } - - public void updateActionView(RepositorySet set, int index) { - mSet = set; - - mActionViewSetValue.setText(String.valueOf(index)); - - mActionViewWeightValue.setText(String.valueOf(set.getWeight())); - mActionViewWeightDescription.setText(formatWeightDescription()); - - mActionViewRepsValue.setText(String.valueOf(set.getReps())); - mActionViewRepsDescription.setText("Reps"); - } - - public void updateActionViewForTimedSet(RepositorySet set, int index) { - mSet = set; - - mActionViewSetValue.setText(String.valueOf(index)); - - mActionViewRepsValue.setText(formatMinutes(mSet.getSeconds())); - mActionViewRepsDescription.setText("Minutes"); - - mActionViewWeightValue.setText(formatSeconds(mSet.getSeconds())); - mActionViewWeightDescription.setText("Seconds"); - } - - public void updateSet(RepositorySet set, View view) { - TextView repsOnlyValue = (TextView) view.findViewById(R.id.repsOnlyValue); - - TextView reps = (TextView) view.findViewById(R.id.repsValue); - TextView weight = (TextView) view.findViewById(R.id.weightValue); - - View center = view.findViewById(R.id.center); - - if(set.getWeight() == 0) { - repsOnlyValue.setVisibility(View.VISIBLE); - - reps.setVisibility(View.GONE); - weight.setVisibility(View.GONE); - - center.setVisibility(View.GONE); - - repsOnlyValue.setText(formatReps(set.getReps(), false)); - } else { - repsOnlyValue.setVisibility(View.GONE); - - reps.setVisibility(View.VISIBLE); - weight.setVisibility(View.VISIBLE); - - center.setVisibility(View.VISIBLE); - - reps.setText(formatReps(set.getReps(), true)); - weight.setText(formatWeight(set.getWeight())); - } - } - - public void updateTimedSet(RepositorySet set, View view) { - TextView secondsOnlyValue = (TextView) view.findViewById(R.id.secondsOnlyValue); - - TextView minutes = (TextView) view.findViewById(R.id.minutesValue); - TextView seconds = (TextView) view.findViewById(R.id.secondsValue); - - View center = view.findViewById(R.id.center); - - if(set.getSeconds() < 60) { - secondsOnlyValue.setVisibility(View.VISIBLE); - - minutes.setVisibility(View.GONE); - seconds.setVisibility(View.GONE); - - center.setVisibility(View.GONE); - - secondsOnlyValue.setText(formatSeconds( - set.getSeconds() - ) + "s"); - } else { - secondsOnlyValue.setVisibility(View.GONE); - - minutes.setVisibility(View.VISIBLE); - seconds.setVisibility(View.VISIBLE); - - center.setVisibility(View.VISIBLE); - - minutes.setText(formatMinutes(set.getSeconds()) + "m"); - seconds.setText(formatSeconds(set.getSeconds()) + "s"); - } - } - - public void updateToolbarMenu() { - int numberOfSets = mViewSets.size(); - - Menu menu = mToolbar.getMenu(); - - if(numberOfSets >= Constants.INSTANCE.getMAXIMUM_NUMBER_OF_SETS()) { - menu.findItem(R.id.action_add_set).setVisible(false); - menu.findItem(R.id.action_add_timed_set).setVisible(false); - } else if (numberOfSets == 1) { - if (mRepositoryExercise.getDefaultSet().equals("timed")) { - menu.findItem(R.id.action_add_set).setVisible(false); - menu.findItem(R.id.action_add_timed_set).setVisible(true); - } else { - menu.findItem(R.id.action_add_set).setVisible(true); - menu.findItem(R.id.action_add_timed_set).setVisible(false); - } - - menu.findItem(R.id.action_remove_last_set).setVisible(false); - } else { - if (mRepositoryExercise.getDefaultSet().equals("timed")) { - menu.findItem(R.id.action_add_set).setVisible(false); - menu.findItem(R.id.action_add_timed_set).setVisible(true); - } else { - menu.findItem(R.id.action_add_set).setVisible(true); - menu.findItem(R.id.action_add_timed_set).setVisible(false); - } - - menu.findItem(R.id.action_remove_last_set).setVisible(true); - } - } - - public void inflateToolbarMenu() { - mToolbar.inflateMenu(R.menu.menu_log_workout); - mToolbar.setOnMenuItemClickListener((item) -> { - switch (item.getItemId()) { - case R.id.action_add_set: { - if (shouldAddSet()) { - int seconds = 0; - double weight = 0; - int reps = 0; - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if (!mRepositoryExercise.getSets().isEmpty()) { - RepositorySet repositorySet = mRepositoryExercise.getSets().last(); - seconds = repositorySet.getSeconds(); - weight = repositorySet.getWeight(); - reps = repositorySet.getReps(); - } - - RepositorySet repositorySet = realm.createObject(RepositorySet.class); - - repositorySet.setId("Set-" + UUID.randomUUID().toString()); - repositorySet.setIsTimed(false); - repositorySet.setSeconds(seconds); - repositorySet.setWeight(weight); - repositorySet.setReps(reps); - - repositorySet.setExercise(mRepositoryExercise); - - realm.commitTransaction(); - - addSet(repositorySet); - updateToolbarMenu(); - } - - return true; - } - - case R.id.action_add_timed_set: { - if (shouldAddSet()) { - int seconds = 0; - double weight = 0; - int reps = 0; - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if (!mRepositoryExercise.getSets().isEmpty()) { - RepositorySet repositorySet = mRepositoryExercise.getSets().last(); - seconds = repositorySet.getSeconds(); - weight = repositorySet.getWeight(); - reps = repositorySet.getReps(); - } - - RepositorySet repositorySet = realm.createObject(RepositorySet.class); - - repositorySet.setId("Set-" + UUID.randomUUID().toString()); - repositorySet.setIsTimed(true); - repositorySet.setSeconds(seconds); - repositorySet.setWeight(weight); - repositorySet.setReps(reps); - - repositorySet.setExercise(mRepositoryExercise); - - realm.commitTransaction(); - - addTimedSet(repositorySet); - updateToolbarMenu(); - } - - return true; - } - - case R.id.action_remove_last_set: { - if (shouldRemoveSet()) { - removeLastSet(); - updateToolbarMenu(); - } - - return true; - } - } - - return false; - }); - - updateToolbarMenu(); - } - - public void invalidateToolbarMenu() { - mToolbar.getMenu().clear(); - } - - public void onClickIncreaseWeight() { - if(mSet.isTimed()) { - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if(mSet.getSeconds() % 60 == 59) { - mSet.setSeconds(mSet.getSeconds() - 59); - } else { - mSet.setSeconds(mSet.getSeconds() + 1); - } - - realm.commitTransaction(); - - mActionViewRepsValue.setText(formatMinutes(mSet.getSeconds())); - mActionViewWeightValue.setText(formatSeconds(mSet.getSeconds())); - - setLastUpdatedTime(); - } else { - if(mSet.getWeight() >= 250) { - return; - } - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if (mWeightMeasurementUnit.equals(WeightMeasurementUnit.kg)) { - mSet.setWeight(mSet.getWeight() + 0.5); - } else { - mSet.setWeight(mSet.getWeight() + 1.0); - } - - realm.commitTransaction(); - - mActionViewWeightValue.setText(String.valueOf(mSet.getWeight())); - - setLastUpdatedTime(); - } - } - - public void onClickDecreaseWeight() { - if(mSet.isTimed()) { - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if(mSet.getSeconds() % 60 == 0) { - mSet.setSeconds(mSet.getSeconds() + 59); - } else { - mSet.setSeconds(mSet.getSeconds() - 1); - } - - realm.commitTransaction(); - - mActionViewRepsValue.setText(formatMinutes(mSet.getSeconds())); - mActionViewWeightValue.setText(formatSeconds(mSet.getSeconds())); - - setLastUpdatedTime(); - } else { - if(mSet.getWeight() <= 0) { - return; - } - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - if (mWeightMeasurementUnit.equals(WeightMeasurementUnit.kg)) { - mSet.setWeight(mSet.getWeight() - 0.5); - } else { - mSet.setWeight(mSet.getWeight() - 1.0); - } - - realm.commitTransaction(); - - mActionViewWeightValue.setText(String.valueOf(mSet.getWeight())); - - setLastUpdatedTime(); - } - } - - public void onClickIncreaseReps() { - if(mSet.isTimed()) { - if (mSet.getSeconds() / 60 >= 5) { - return; - } - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - mSet.setSeconds(mSet.getSeconds() + 60); - - realm.commitTransaction(); - - mActionViewRepsValue.setText(formatMinutes(mSet.getSeconds())); - mActionViewWeightValue.setText(formatSeconds(mSet.getSeconds())); - - setLastUpdatedTime(); - } else { - if(mSet.getReps() >= 50) { - return; - } - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - mSet.setReps(mSet.getReps() + 1); - - realm.commitTransaction(); - - mActionViewRepsValue.setText(String.valueOf(mSet.getReps())); - - setLastUpdatedTime(); - } - } - - public void onClickDecreaseReps() { - if(mSet.isTimed()) { - if(mSet.getSeconds() >= 60) { - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - mSet.setSeconds(mSet.getSeconds() - 60); - - realm.commitTransaction(); - } - - mActionViewRepsValue.setText(formatMinutes(mSet.getSeconds())); - mActionViewWeightValue.setText(formatSeconds(mSet.getSeconds())); - - setLastUpdatedTime(); - } else { - if(mSet.getReps() == 0) { - return; - } - - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - mSet.setReps(mSet.getReps() - 1); - - realm.commitTransaction(); - - mActionViewRepsValue.setText(String.valueOf(mSet.getReps())); - - setLastUpdatedTime(); - } - } - - public String formatReps(int reps, boolean append) { - if(append) { - return reps + " x"; - } else { - if(reps == 0) { - return "/"; - } - - return String.valueOf(reps); - } - } - - public String formatWeight(double weight) { - return String.format("%s %s", weight, mWeightMeasurementUnit.toString()); - } - - public String formatWeightDescription() { - return String.format("Weight (%s)", mWeightMeasurementUnit.toString()); - } - - public String formatMinutes(int seconds) { - int minutes = seconds / 60; - - if(minutes == 0) { - return "0"; - } - - return String.valueOf(minutes); - } - - public String formatSeconds(int seconds) { - int s = seconds % 60; - - if(s == 0) { - return "0"; - } - - return String.valueOf(s); - } - - public boolean isCompleted(RepositoryExercise repositoryExercise) { - int size = repositoryExercise.getSets().size(); - - if (size == 0) { - return false; - } - - RepositorySet firstSet = repositoryExercise.getSets().get(0); - - if(size == 1 && firstSet.getSeconds() == 0 && firstSet.getReps() == 0) { - return false; - } - - return true; - } - - public void setLastUpdatedTime() { - /** - * Update last updated time. - * - * Update only if the difference between start time and last updated time is less than - * 120 minutes (it ignores changing the time later in the day after the workout ends - * if we decide to update some values). - */ - if (mRepositoryRoutine != null) { - DateTime startTime = new DateTime(mRepositoryRoutine.getStartTime()); - DateTime lastUpdatedTime = new DateTime(mRepositoryRoutine.getLastUpdatedTime()); - - Duration duration = new Duration(startTime, lastUpdatedTime); - - if (duration.toStandardMinutes().getMinutes() < 120) { - Realm realm = RepositoryStream.getInstance().getRealm(); - realm.beginTransaction(); - - mRepositoryRoutine.setLastUpdatedTime(new DateTime().toDate()); - - realm.commitTransaction(); - } - } - } -} diff --git a/app/src/main/java/com/bodyweight/fitness/view/dialog/ProgressDialog.java b/app/src/main/java/com/bodyweight/fitness/view/dialog/ProgressDialog.java deleted file mode 100644 index fa749590..00000000 --- a/app/src/main/java/com/bodyweight/fitness/view/dialog/ProgressDialog.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.bodyweight.fitness.view.dialog; - -import android.app.Dialog; -import android.graphics.Color; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.support.v7.widget.Toolbar; -import android.view.View; -import android.view.Window; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.TextView; - -import com.bodyweight.fitness.Constants; -import com.bodyweight.fitness.model.Exercise; -import com.bodyweight.fitness.model.Routine; -import com.bodyweight.fitness.model.SectionMode; -import com.bodyweight.fitness.stream.RoutineStream; -import com.bodyweight.fitness.view.widget.CircularProgressBar; - -import butterknife.ButterKnife; -import butterknife.InjectView; - -import com.bodyweight.fitness.R; - -public class ProgressDialog extends DialogFragment { - @InjectView(R.id.toolbar) - Toolbar mToolbar; - - @InjectView(R.id.level_previous_button) - ImageButton mLevelPreviousButton; - - @InjectView(R.id.level_next_button) - ImageButton mLevelNextButton; - - @InjectView(R.id.level_progress_bar) - CircularProgressBar mLevelProgressBar; - - @InjectView(R.id.level_text_view) - TextView mLevelTextView; - - @InjectView(R.id.level_confirm_button) - Button mLevelConfirmButton; - - private Exercise mExercise; - - private int mAvailableLevels; - private int mChosenLevel = 0; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - String exerciseId = getArguments().getString(Constants.INSTANCE.getExerciseId()); - - Routine routine = RoutineStream.getInstance().getRoutine(); - - for (Exercise exercise : routine.getExercises()) { - if (exercise.getExerciseId().equals(exerciseId)) { - mExercise = exercise; - mAvailableLevels = mExercise.getSection().getAvailableLevels(); - mChosenLevel = mExercise.getSection().getCurrentLevel(); - - break; - } - } - - Dialog dialog = new Dialog(getContext()); - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - dialog.setContentView(R.layout.view_dialog_progress); - dialog.setCanceledOnTouchOutside(true); - - ButterKnife.inject(this, dialog); - - mLevelPreviousButton.setOnClickListener(v -> { - mChosenLevel = mChosenLevel - 1; - updateDialog(); - }); - - mLevelNextButton.setOnClickListener(v -> { - mChosenLevel = mChosenLevel + 1; - updateDialog(); - }); - - mLevelConfirmButton.setOnClickListener(v -> { - Exercise chosenExercise = mExercise.getSection().getExercises().get(mChosenLevel); - - RoutineStream.getInstance().setLevel(chosenExercise, mChosenLevel); - - dialog.dismiss(); - }); - - mLevelProgressBar.setWheelSize(12); - mLevelProgressBar.setProgressColor(Color.parseColor("#009688")); // 00453E - mLevelProgressBar.setProgressBackgroundColor(Color.parseColor("#00453E")); - - updateDialog(); - - return dialog; - } - - private void updateDialog() { - Exercise chosenExercise = mExercise.getSection().getExercises().get(mChosenLevel); - - mToolbar.setTitle(chosenExercise.getTitle()); - mToolbar.setSubtitle(chosenExercise.getDescription()); - - if(mExercise.getSection().getSectionMode() == SectionMode.LEVELS) { - mLevelTextView.setText(chosenExercise.getLevel()); - } else { - mLevelTextView.setText("Pick One"); - } - - if(mChosenLevel == 0) { - mLevelPreviousButton.setVisibility(View.INVISIBLE); - } else { - mLevelPreviousButton.setVisibility(View.VISIBLE); - } - - if(mChosenLevel >= (mAvailableLevels - 1)) { - mLevelNextButton.setVisibility(View.INVISIBLE); - } else { - mLevelNextButton.setVisibility(View.VISIBLE); - } - - updateProgressBar(); - } - - private void updateProgressBar() { - mLevelProgressBar.setProgress((1f / mAvailableLevels) * (mChosenLevel + 1)); - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/App.kt b/app/src/main/kotlin/com/bodyweight/fitness/App.kt index eefbed2a..7efa9028 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/App.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/App.kt @@ -3,7 +3,8 @@ package com.bodyweight.fitness import android.app.Application import android.content.Context -import com.bodyweight.fitness.model.persistence.Glacier +import com.bodyweight.fitness.persistence.Glacier +import com.bodyweight.fitness.repository.SchemaMigration import com.crashlytics.android.Crashlytics import com.crashlytics.android.answers.Answers @@ -25,7 +26,7 @@ class App : Application() { Glacier.init(applicationContext) JodaTimeAndroid.init(applicationContext) - val config = RateThisApp.Config(3, 5) + val config = RateThisApp.Config(5, 45) RateThisApp.init(config) if (!BuildConfig.DEBUG) { diff --git a/app/src/main/kotlin/com/bodyweight/fitness/Constants.kt b/app/src/main/kotlin/com/bodyweight/fitness/Constants.kt index 8f42acdb..a3f08140 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/Constants.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/Constants.kt @@ -4,14 +4,17 @@ object Constants { val primaryKeyRoutineId = "primaryKeyRoutineId" val exerciseId = "exerciseId" - val MAXIMUM_NUMBER_OF_SETS = 12 - val PRIMARY_KEY_ROUTINE_ID = "primaryKeyRoutineId" + val googlePlayUrl = "https://play.google.com/store/apps/details?id=com.bodyweight.fitness.pro" + val fileProvider = "com.bodyweight.fitness.fileprovider" - val PREFERENCE_DEFAULT_ROUTINE = "PREFERENCE_DEFAULT_ROUTINE" - val PREFERENCE_WEIGHT_MEASUREMENT_UNITS = "PREFERENCE_WEIGHT_MEASUREMENT_UNITS" - val PREFERENCE_PLAY_SOUND_WHEN_TIMER_STOPS = "PREFERENCE_PLAY_SOUND_WHEN_TIMER_STOPS" - val PREFERENCE_AUTOMATICALLY_LOG_WORKOUT_TIME = "PREFERENCE_AUTOMATICALLY_LOG_WORKOUT_TIME" - val PREFERENCE_KEEP_SCREEN_ON = "PREFERENCE_KEEP_SCREEN_ON" - val PREFERENCE_TIMER_KEY = "PREFERENCE_TIMER_KEY_" - val PREFERENCE_NUMBER_OF_REPS_KEY = "PREFERENCE_NUMBER_OF_REPS_KEY_" + val maximumNumberOfSets = 12 + + val preferencesDefaultRoutineKey = "PREFERENCE_DEFAULT_ROUTINE" + val preferencesWeightMeasurementUnitsKey = "PREFERENCE_WEIGHT_MEASUREMENT_UNITS" + val preferencesPlaySoundWhenTimerStopsKey = "PREFERENCE_PLAY_SOUND_WHEN_TIMER_STOPS" + val preferencesAutomaticallyLogWorkoutTimeKey = "PREFERENCE_AUTOMATICALLY_LOG_WORKOUT_TIME" + val preferencesKeepScreenOnKey = "PREFERENCE_KEEP_SCREEN_ON" + val preferencesTimerKey = "PREFERENCE_TIMER_KEY_" + val preferencesNumberOfRepsKey = "PREFERENCE_NUMBER_OF_REPS_KEY_" + val preferencesIntroductionShown = "PREFERENCE_INTRODUCTION_SHOWN" } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/Extensions.kt b/app/src/main/kotlin/com/bodyweight/fitness/Extensions.kt index eb38d1b4..63d47684 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/Extensions.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/Extensions.kt @@ -1,34 +1,52 @@ package com.bodyweight.fitness +import android.content.Context +import android.graphics.Color +import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout -import com.bodyweight.fitness.model.repository.RepositoryExercise -import com.bodyweight.fitness.model.repository.RepositoryRoutine -import com.bodyweight.fitness.stream.RepositoryStream +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.utils.Preferences import io.realm.RealmResults import org.joda.time.DateTime -object Exercise { - fun isCompleted(repositoryExercise: RepositoryExercise): Boolean { - val size = repositoryExercise.sets.size +fun primary(): Int { + return Color.parseColor("#009688") +} - if (size == 0) { - return false - } +fun primaryDark(): Int { + return Color.parseColor("#00453E") +} - val firstSet = repositoryExercise.sets[0] +fun calculateLayoutWeight(completionRate: Int): Float { + if (completionRate <= 10) { + return 7f + } - if (size == 1 && firstSet.seconds == 0 && firstSet.reps == 0) { - return false - } + val weight = completionRate * 0.7f; - return true + if (weight > 70f) { + return 70f } + + return weight } +fun LinearLayout.setLayoutWeight(weight: Float) { + val params = this.layoutParams as LinearLayout.LayoutParams + + params.weight = weight; + + this.layoutParams = params +} + +fun Int.toPx(context: Context): Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics).toInt() + fun View.setBackgroundResourceWithPadding(resource: Int) { val bottom = paddingBottom val top = paddingTop @@ -43,6 +61,22 @@ fun ViewGroup.inflate(layoutRes: Int, attachToRoot: Boolean = false): View { return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot) } +fun View.inflate(layoutRes: Int, root: ViewGroup, attachToRoot: Boolean = false): View { + return LayoutInflater.from(context).inflate(layoutRes, root, attachToRoot) +} + +fun View.setVisible() { + this.visibility = View.VISIBLE +} + +fun View.setInvisible() { + this.visibility = View.INVISIBLE +} + +fun View.setGone() { + this.visibility = View.GONE +} + fun DateTime.isToday(): Boolean { val now = DateTime() @@ -60,7 +94,7 @@ fun DateTime.isRoutineLogged(): Boolean { .minusSeconds(1) .toDate() - val realm = RepositoryStream.getInstance().realm + val realm = Repository.realm val routine = realm.where(RepositoryRoutine::class.java) .between("startTime", start, end) .findFirst() @@ -77,7 +111,7 @@ fun DateTime.isRoutineLoggedWithResults(): RealmResults { .minusSeconds(1) .toDate() - val realm = RepositoryStream.getInstance().realm + val realm = Repository.realm val results: RealmResults = realm.where(RepositoryRoutine::class.java) .between("startTime", start, end) .findAll() @@ -85,6 +119,22 @@ fun DateTime.isRoutineLoggedWithResults(): RealmResults { return results } +fun Double.formatWeight(): String { + return "$this ${Preferences.weightMeasurementUnit.asString}" +} + +fun Int.formatReps(append: Boolean = false): String { + if (append) { + return "$this x" + } else { + if (this == 0) { + return "/" + } + + return this.toString() + } +} + fun Int.formatMinutes(format: Boolean = true): String { val minutes = this / 60 @@ -95,12 +145,26 @@ fun Int.formatMinutes(format: Boolean = true): String { return "0" } else if (minutes < 10) { - return "0" + minutes + if (format) { + return "0" + minutes + } + + return minutes.toString() } return minutes.toString() } +fun Int.formatMinutesPostfix(): String { + val minutes = this / 60 + + if (minutes == 0) { + return "0m" + } + + return "${minutes.toString()}m" +} + fun Int.formatMinutesAsNumber(): Int { return this / 60 } @@ -115,12 +179,26 @@ fun Int.formatSeconds(format: Boolean = true): String { return "0" } else if (seconds < 10) { - return "0" + seconds + if (format) { + return "0" + seconds + } + + return seconds.toString() } return seconds.toString() } +fun Int.formatSecondsPostfix(): String { + val seconds = this % 60 + + if (seconds == 0) { + return "0s" + } + + return "${seconds.toString()}s" +} + fun Int.formatSecondsAsNumber(): Int { return this % 60 } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarListAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarListAdapter.kt index fb44e64e..58329ab1 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarListAdapter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarListAdapter.kt @@ -1,36 +1,37 @@ package com.bodyweight.fitness.adapter import android.content.Intent +import android.support.v4.content.FileProvider import android.support.v7.app.AlertDialog import android.support.v7.widget.RecyclerView import android.view.View import android.view.ViewGroup -import com.bodyweight.fitness.* +import android.widget.Toast -import com.bodyweight.fitness.model.repository.RepositoryRoutine -import com.bodyweight.fitness.stream.RepositoryStream +import com.bodyweight.fitness.* +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.repository.Repository import com.bodyweight.fitness.stream.Stream import com.bodyweight.fitness.ui.ProgressActivity -import com.bodyweight.fitness.utils.PreferenceUtils - -import org.joda.time.DateTime -import org.joda.time.Duration -import org.joda.time.format.PeriodFormatterBuilder import io.realm.RealmResults + import kotlinx.android.synthetic.main.view_calendar_card.view.* +import java.io.File +import java.io.FileOutputStream + class CalendarListAdapter : RecyclerView.Adapter() { - private var mResults: RealmResults? = null + private var repositoryRoutineList: RealmResults? = null fun setItems(results: RealmResults) { - mResults = results + repositoryRoutineList = results notifyDataSetChanged() } - fun removeItems() { - mResults = null + fun removeItem(repositoryRoutine: RepositoryRoutine) { + repositoryRoutineList?.remove(repositoryRoutine) notifyDataSetChanged() } @@ -42,13 +43,13 @@ class CalendarListAdapter : RecyclerView.Adapter() { } override fun onBindViewHolder(presenter: CalendarRoutinePresenter, position: Int) { - mResults?.let { + repositoryRoutineList?.let { presenter.calendarListAdapter = this presenter.onBindView(it[position]) } } - override fun getItemCount(): Int = mResults?.size ?: 0 + override fun getItemCount(): Int = repositoryRoutineList?.size ?: 0 override fun getItemViewType(position: Int): Int = 0 } @@ -59,116 +60,72 @@ class CalendarRoutinePresenter(itemView: View) : RecyclerView.ViewHolder(itemVie itemView.view_calendar_routine_title.text = repositoryRoutine.title itemView.view_calendar_routine_subtitle.text = repositoryRoutine.subtitle + val completionRate = RepositoryRoutine.getCompletionRate(repositoryRoutine) + + itemView.completion_rate_label.text = completionRate.label + itemView.completion_rate_value.setLayoutWeight(calculateLayoutWeight(completionRate.percentage)) + itemView.view_calendar_card_view_button.setOnClickListener { val intent = Intent(it.context, ProgressActivity::class.java) - intent.putExtra(Constants.PRIMARY_KEY_ROUTINE_ID, repositoryRoutine.id) + intent.putExtra(Constants.primaryKeyRoutineId, repositoryRoutine.id) it.context.startActivity(intent) } itemView.view_calendar_card_export_button.setOnClickListener { val intent = Intent(it.context, ProgressActivity::class.java) - intent.putExtra(Constants.PRIMARY_KEY_ROUTINE_ID, repositoryRoutine.id) + intent.putExtra(Constants.primaryKeyRoutineId, repositoryRoutine.id) it.context.startActivity(intent) } itemView.view_calendar_card_export_button.setOnClickListener { - val title = exportTitle(repositoryRoutine) - val content = exportHTML(repositoryRoutine) + val context = it.context + + try { + val path = File(context.filesDir, "csv"); + val file = File(path, "LoggedWorkout.csv").apply { + if (parentFile.mkdirs()) { + createNewFile() + } + } + + FileOutputStream(file).apply { + write(RepositoryRoutine.toCSV(repositoryRoutine).toByteArray()) + flush() + close() + } - val sendIntent = Intent() + context.startActivity(Intent().apply { + action = Intent.ACTION_SEND + type = "text/plain" + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION - sendIntent.action = Intent.ACTION_SEND - sendIntent.putExtra(Intent.EXTRA_TITLE, title) - sendIntent.putExtra(Intent.EXTRA_TEXT, content) - sendIntent.type = "text/plain" + putExtra(Intent.EXTRA_TITLE, RepositoryRoutine.getTitleWithDate(repositoryRoutine)) + putExtra(Intent.EXTRA_TEXT, RepositoryRoutine.toText(repositoryRoutine)) + putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context, Constants.fileProvider, file)) + }) + } catch (e: Exception) { + e.printStackTrace() - it.context.startActivity(sendIntent) + Toast.makeText(context, "Error: Unable to export workout log", Toast.LENGTH_SHORT).show() + } } itemView.view_calendar_card_remove_button.setOnClickListener { AlertDialog.Builder(it.context) .setTitle("Remove Logged Workout?") .setPositiveButton("Ok") { dialog, which -> - val realm = RepositoryStream.getInstance().realm + val realm = Repository.realm realm.executeTransaction { repositoryRoutine.deleteFromRealm() } - calendarListAdapter?.removeItems() - Stream.setRepository() } .setNegativeButton("Cancel") { dialog, which -> } .show() } } - - private fun exportTitle(repositoryRoutine: RepositoryRoutine): String { - var title = "" - - title += String.format("%s - %s - %s.", - repositoryRoutine.title, - repositoryRoutine.subtitle, - DateTime(repositoryRoutine.startTime).toString("EEEE, d MMMM YYYY - HH:mm")) - - return title - } - - private fun exportHTML(repositoryRoutine: RepositoryRoutine): String { - var content = "" - - content += "Hello, The following is your workout in Text/HTML format." - - content += String.format("\n\nWorkout on %s.", DateTime(repositoryRoutine.startTime).toString("EEEE, d MMMM YYYY - HH:mm")) - content += String.format("\nLast Updated at %s.", DateTime(repositoryRoutine.lastUpdatedTime).toString("HH:mm")) - content += String.format("\nWorkout length: %s.", workoutLength(repositoryRoutine)) - content += String.format("\n\n%s - %s", repositoryRoutine.title, repositoryRoutine.subtitle) - - for (exercise in repositoryRoutine.exercises) { - if (exercise.isVisible) { - content += String.format("\n\n%s", exercise.title) - - val weightUnit = PreferenceUtils.getInstance().weightMeasurementUnit.toString() - - var index = 0 - for (set in exercise.sets) { - index++ - - content += String.format("\nSet %s", index) - - if (set.isTimed) { - content += String.format("\nMinutes: %s", set.seconds.formatMinutes(false)) - content += String.format("\nSeconds: %s", set.seconds.formatSeconds(false)) - } else { - content += String.format("\nReps: %s", set.reps) - content += String.format("\nWeight: %s %s", set.weight, weightUnit) - } - } - } - } - - return content - } - - fun workoutLength(repositoryRoutine: RepositoryRoutine): String { - val startTime = DateTime(repositoryRoutine.startTime) - val lastUpdatedTime = DateTime(repositoryRoutine.lastUpdatedTime) - - val duration = Duration(startTime, lastUpdatedTime) - - if (duration.toStandardMinutes().minutes < 10) { - return "--" - } else { - return PeriodFormatterBuilder() - .appendHours() - .appendSuffix("h ") - .appendMinutes() - .appendSuffix("m") - .toFormatter() - .print(duration.toPeriod()) - } - } } diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarPagerAdapter.kt similarity index 85% rename from app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarAdapter.kt rename to app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarPagerAdapter.kt index 5c95e771..59cfc4fa 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarAdapter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/CalendarPagerAdapter.kt @@ -12,7 +12,7 @@ import com.bodyweight.fitness.view.widget.ViewPager import com.bodyweight.fitness.R import com.bodyweight.fitness.inflate -class CalendarAdapter : PagerAdapter() { +class CalendarPagerAdapter : PagerAdapter() { companion object { val DEFAULT_POSITION = 60 } @@ -21,9 +21,9 @@ class CalendarAdapter : PagerAdapter() { val viewPager = viewGroup as ViewPager val calendarPageView = viewGroup.inflate(R.layout.view_calendar_page) as CalendarPageView - val calendarPagePresenter = calendarPageView.mPresenter as CalendarPagePresenter + val calendarPagePresenter = calendarPageView.presenter as CalendarPagePresenter - calendarPagePresenter.mViewPagerPosition = position + calendarPagePresenter.viewPagerPosition = position calendarPageView.updateView() viewPager.addView(calendarPageView) diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/DashboardAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/DashboardTreeAdapter.kt similarity index 86% rename from app/src/main/kotlin/com/bodyweight/fitness/adapter/DashboardAdapter.kt rename to app/src/main/kotlin/com/bodyweight/fitness/adapter/DashboardTreeAdapter.kt index af7421cf..39909a37 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/adapter/DashboardAdapter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/DashboardTreeAdapter.kt @@ -6,18 +6,13 @@ import android.view.ViewGroup import com.bodyweight.fitness.R import com.bodyweight.fitness.inflate -import com.bodyweight.fitness.model.Category -import com.bodyweight.fitness.model.Exercise -import com.bodyweight.fitness.model.LinkedRoutine -import com.bodyweight.fitness.model.Routine -import com.bodyweight.fitness.model.RoutineType -import com.bodyweight.fitness.model.Section -import com.bodyweight.fitness.model.SectionMode +import com.bodyweight.fitness.model.* import kotlinx.android.synthetic.main.view_dashboard_category.view.* import kotlinx.android.synthetic.main.view_dashboard_double_item.view.* import kotlinx.android.synthetic.main.view_dashboard_section.view.* import kotlinx.android.synthetic.main.view_dashboard_single_item.view.* + import rx.Observable import rx.subjects.PublishSubject @@ -26,7 +21,7 @@ import java.util.HashSet data class Tuple(val left: LinkedRoutine? = null, val right: LinkedRoutine? = null) -class DashboardAdapter(private val routine: Routine, currentExercise: Exercise) : RecyclerView.Adapter() { +class DashboardTreeAdapter(private val routine: Routine, currentExercise: Exercise) : RecyclerView.Adapter() { private val dashboardTree = HashMap() private val exerciseSubject = PublishSubject.create() @@ -48,7 +43,7 @@ class DashboardAdapter(private val routine: Routine, currentExercise: Exercise) } else { val category = exercise.category if (!categorySet.contains(category)) { - categorySet.add(category) + categorySet.add(category!!) dashboardTree.put(index, Tuple(category)) @@ -57,7 +52,7 @@ class DashboardAdapter(private val routine: Routine, currentExercise: Exercise) val section = exercise.section if (!set.contains(section)) { - set.add(section) + set.add(section!!) dashboardTree.put(index, Tuple(section)) @@ -72,9 +67,9 @@ class DashboardAdapter(private val routine: Routine, currentExercise: Exercise) firstInSection = false } - if (exercise.section.sectionMode == SectionMode.ALL + if (exercise.section!!.sectionMode == SectionMode.All && exercise.next != null - && exercise.next.section == exercise.section + && exercise!!.next!!.section == exercise.section && !firstInSection) { dashboardTree.put(index, Tuple(exercise, exercise.next)) @@ -123,11 +118,11 @@ class DashboardAdapter(private val routine: Routine, currentExercise: Exercise) override fun getItemViewType(position: Int): Int { val tuple = dashboardTree[position] as Tuple - if (tuple.left?.type == RoutineType.SECTION) { + if (tuple.left?.type == RoutineType.Section) { return 1 } - if (tuple.left?.type == RoutineType.CATEGORY) { + if (tuple.left?.type == RoutineType.Category) { return 3 } @@ -159,7 +154,7 @@ class DashboardSectionPresenter(itemView: View) : DashboardAbstractPresenter(ite override fun onBindView(exerciseSubject: PublishSubject, tuple: Tuple) { val section = tuple.left as Section - if (section.sectionMode == SectionMode.ALL) { + if (section.sectionMode == SectionMode.All) { itemView.section_title.text = section.title } else { itemView.section_title.text = section.title @@ -179,10 +174,10 @@ class DashboardSingleItemPresenter(itemView: View) : DashboardAbstractPresenter( itemView.exercise_button.setBackgroundDrawable(itemView.context.resources.getDrawable(R.drawable.dashboard_circle_weighted)) } - if (exercise.section.sectionMode == SectionMode.LEVELS) { + if (exercise.section!!.sectionMode == SectionMode.Levels) { itemView.exercise_title.text = exercise.title - itemView.exercise_level.text = String.format("%s/%s", exercise.level, exercise.section.exercises.size) + itemView.exercise_level.text = String.format("%s out of %s", exercise.level, exercise.section!!.exercises.size) itemView.exercise_level.visibility = View.VISIBLE } else { itemView.exercise_title.text = exercise.title diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/GraphAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/GraphAdapter.kt new file mode 100644 index 00000000..cfccd1bf --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/GraphAdapter.kt @@ -0,0 +1,234 @@ +package com.bodyweight.fitness.adapter + +import com.bodyweight.fitness.model.* +import com.robinhood.spark.SparkAdapter + +import java.util.* + +class RepsAdapter : SparkAdapter() { + private var data: ArrayList? = null + + fun changeData(data: ArrayList) { + this.data = data + + notifyDataSetChanged() + } + + override fun getCount(): Int { + data?.let { + return it.size + } + + return 0 + } + + override fun getItem(index: Int): Any { + val item = data?.getOrNull(index) + + item?.let { + return it + } + + return "" + } + + override fun getY(index: Int): Float { + data?.getOrNull(index)?.repositorySet?.let { + return it.reps.toFloat() + } + + return 0f + } + + override fun getX(index: Int): Float { + return index.toFloat() + } + + override fun hasBaseLine(): Boolean { + return true + } + + override fun getBaseLine(): Float { + return 15f + } +} + +class TimeAdapter : SparkAdapter() { + private var data: ArrayList? = ArrayList() + + fun changeData(data: ArrayList) { + this.data = data + + notifyDataSetChanged() + } + + override fun getCount(): Int { + data?.let { + return it.size + } + + return 0 + } + + override fun getItem(index: Int): Any { + val item = data?.getOrNull(index) + + item?.let { + return it + } + + return "" + } + + override fun getY(index: Int): Float { + data?.getOrNull(index)?.repositorySet?.let { + return it.seconds.toFloat() + } + + return 0f + } + + override fun getX(index: Int): Float { + return index.toFloat() + } + + override fun hasBaseLine(): Boolean { + return true + } + + override fun getBaseLine(): Float { + return 120f + } +} + +class WorkoutLengthAdapter : SparkAdapter() { + private var data = ArrayList() + + fun changeData(data: ArrayList) { + this.data = data + + notifyDataSetChanged() + } + + override fun getCount(): Int { + return data.size + } + + override fun getItem(index: Int): Any { + val item = data.getOrNull(index) + + item?.let { + return it + } + + return "" + } + + override fun getY(index: Int): Float { + data[index].repositoryRoutine?.let { + return RepositoryRoutine.getWorkoutLengthInMinutes(it).toFloat() + } + + return 0f + } + + override fun getX(index: Int): Float { + return index.toFloat() + } + + override fun hasBaseLine(): Boolean { + return true + } + + override fun getBaseLine(): Float { + return 60.0f + } +} + +class CategoryCompletionRateAdapter : SparkAdapter() { + private var data = ArrayList() + + fun changeData(data: ArrayList) { + this.data = data + + notifyDataSetChanged() + } + + override fun getCount(): Int { + return data.size + } + + override fun getItem(index: Int): Any { + val item = data.getOrNull(index) + + item?.let { + return it + } + + return "" + } + + override fun getY(index: Int): Float { + data[index].repositoryCategory?.let { + return RepositoryCategory.getCompletionRate(it).percentage.toFloat() + } + + return 0f + } + + override fun getX(index: Int): Float { + return index.toFloat() + } + + override fun hasBaseLine(): Boolean { + return true + } + + override fun getBaseLine(): Float { + return 100.0f + } +} + +class CompletionRateAdapter : SparkAdapter() { + private var data = ArrayList() + + fun changeData(data: ArrayList) { + this.data = data + + notifyDataSetChanged() + } + + override fun getCount(): Int { + return data.size + } + + override fun getItem(index: Int): Any { + val item = data.getOrNull(index) + + item?.let { + return it + } + + return "" + } + + override fun getY(index: Int): Float { + data[index].repositoryRoutine?.let { + return RepositoryRoutine.getCompletionRate(it).percentage.toFloat() + } + + return 0f + } + + override fun getX(index: Int): Float { + return index.toFloat() + } + + override fun hasBaseLine(): Boolean { + return true + } + + override fun getBaseLine(): Float { + return 100.0f + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressAdapter.kt deleted file mode 100644 index 441fea98..00000000 --- a/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressAdapter.kt +++ /dev/null @@ -1,128 +0,0 @@ -package com.bodyweight.fitness.adapter - -import android.support.v7.widget.RecyclerView -import android.view.View -import android.view.ViewGroup -import com.bodyweight.fitness.Exercise - -import com.bodyweight.fitness.model.repository.RepositoryCategory -import com.bodyweight.fitness.model.repository.RepositoryExercise -import com.bodyweight.fitness.model.repository.RepositorySection -import com.bodyweight.fitness.stream.DialogType -import com.bodyweight.fitness.stream.UiEvent - -import java.util.HashMap - -import com.bodyweight.fitness.R -import com.bodyweight.fitness.inflate -import kotlinx.android.synthetic.main.activity_progress_card.view.* -import kotlinx.android.synthetic.main.activity_progress_title.view.* - -class ProgressAdapter(private val mRepositoryCategory: RepositoryCategory) : RecyclerView.Adapter() { - private val mItemViewMapping = HashMap() - private val mExerciseViewMapping = HashMap() - - private var mTotalSize = 0 - - init { - /** - * We loop over the sections in order to find out the item view id - * for each section in the Recycler View. - */ - var sectionId = 0 - var exerciseId = 1 - for (repositorySection in mRepositoryCategory.sections) { - mItemViewMapping.put(sectionId, repositorySection) - - var numberOfExercises = 0 - - for (repositoryExercise in repositorySection.exercises) { - if (repositoryExercise.isVisible) { - mExerciseViewMapping.put(exerciseId, repositoryExercise) - - exerciseId += 1 - mTotalSize += 1 - - numberOfExercises++ - } - } - - sectionId = sectionId + numberOfExercises + 1 - exerciseId += 1 - mTotalSize += 1 - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProgressPresenter { - if (viewType == 1) { - val view = parent.inflate(R.layout.activity_progress_title) - - return ProgressTitlePresenter(view) - } - - val view = parent.inflate(R.layout.activity_progress_card) - - return ProgressCardPresenter(view) - } - - override fun onBindViewHolder(holder: ProgressPresenter, position: Int) { - if (mItemViewMapping.containsKey(position)) { - val presenter = holder as ProgressTitlePresenter - - presenter.bindView(mItemViewMapping[position]!!) - } else if (mExerciseViewMapping.containsKey(position)) { - val presenter = holder as ProgressCardPresenter - - presenter.bindView(mExerciseViewMapping[position]!!) - } - } - - override fun getItemCount(): Int { - return mTotalSize - } - - override fun getItemViewType(position: Int): Int { - if (mItemViewMapping.containsKey(position)) { - return 1 - } - - return 0 - } -} - -abstract class ProgressPresenter(itemView: View) : RecyclerView.ViewHolder(itemView) - -class ProgressCardPresenter(itemView: View) : ProgressPresenter(itemView) { - fun bindView(repositoryExercise: RepositoryExercise) { - itemView.toolbar.title = repositoryExercise.title - itemView.toolbar.subtitle = repositoryExercise.description - - if (!Exercise.isCompleted(repositoryExercise)) { - itemView.toolbar.subtitle = "Not completed" - } - - itemView.view_button.setOnClickListener { - UiEvent.showDialog(DialogType.LogWorkout, repositoryExercise.exerciseId) - } - } -} - -class ProgressTitlePresenter(itemView: View) : ProgressPresenter(itemView) { - fun bindView(repositorySection: RepositorySection) { - if (layoutPosition == 0) { - itemView.title.setPadding( - itemView.title.paddingLeft, - itemView.title.paddingLeft, - itemView.title.paddingRight, - itemView.title.paddingBottom) - } else { - itemView.title.setPadding( - itemView.title.paddingLeft, - itemView.title.paddingBottom, - itemView.title.paddingRight, - itemView.title.paddingBottom) - } - - itemView.title.text = repositorySection.title - } -} diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressListAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressListAdapter.kt new file mode 100644 index 00000000..e718c72b --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressListAdapter.kt @@ -0,0 +1,339 @@ +package com.bodyweight.fitness.adapter + +import android.content.Intent +import android.graphics.Color +import android.support.design.widget.TabLayout +import android.support.v7.widget.RecyclerView +import android.view.View +import android.view.ViewGroup +import com.bodyweight.fitness.* + +import com.bodyweight.fitness.model.RepositoryExercise +import com.bodyweight.fitness.model.RepositorySection +import com.bodyweight.fitness.stream.UiEvent +import com.bodyweight.fitness.dialog.LogWorkoutPresenter +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.ui.ProgressExerciseActivity + +import com.trello.rxlifecycle.kotlin.bindToLifecycle +import io.realm.Sort + +import kotlinx.android.synthetic.main.activity_progress_card.view.* +import kotlinx.android.synthetic.main.activity_progress_card_set.view.* +import kotlinx.android.synthetic.main.activity_progress_header.view.* +import kotlinx.android.synthetic.main.activity_progress_title.view.* +import org.joda.time.DateTime +import rx.android.schedulers.AndroidSchedulers +import java.util.* + +enum class ProgressAdapterViewType { + Header, + Section, + Exercise +} + +class ProgressListAdapter(private val repositoryCategory: RepositoryCategory) : RecyclerView.Adapter() { + private val indexViewTypeHashMap = HashMap() + private val indexSectionHashMap = HashMap() + private val indexExerciseHashMap = HashMap() + + init { + var index = 0 + + indexViewTypeHashMap.put(index, ProgressAdapterViewType.Header.ordinal) + + index += 1 + + for (repositorySection in repositoryCategory.sections) { + indexViewTypeHashMap.put(index, ProgressAdapterViewType.Section.ordinal) + indexSectionHashMap.put(index, repositorySection) + + index += 1 + + for (repositoryExercise in RepositoryRoutine.getVisibleAndCompletedExercises(repositorySection.exercises)) { + indexViewTypeHashMap.put(index, ProgressAdapterViewType.Exercise.ordinal) + indexExerciseHashMap.put(index, repositoryExercise) + + index += 1 + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProgressPresenter { + when (viewType) { + ProgressAdapterViewType.Header.ordinal -> { + val view = parent.inflate(R.layout.activity_progress_header) + + return ProgressHeaderPresenter(view) + } + + ProgressAdapterViewType.Section.ordinal -> { + val view = parent.inflate(R.layout.activity_progress_title) + + return ProgressTitlePresenter(view) + } + + else -> { + val view = parent.inflate(R.layout.activity_progress_card) + + return ProgressCardPresenter(view) + } + } + } + + override fun onBindViewHolder(holder: ProgressPresenter, position: Int) { + when (indexViewTypeHashMap[position]) { + ProgressAdapterViewType.Header.ordinal -> { + val presenter = holder as ProgressHeaderPresenter + + presenter.bindView(repositoryCategory) + } + + ProgressAdapterViewType.Section.ordinal -> { + val presenter = holder as ProgressTitlePresenter + + indexSectionHashMap[position]?.let { + presenter.bindView(it) + } + } + + else -> { + val presenter = holder as ProgressCardPresenter + + indexExerciseHashMap[position]?.let { + presenter.bindView(it) + } + } + } + } + + override fun getItemCount(): Int { + return indexViewTypeHashMap.size + } + + override fun getItemViewType(position: Int): Int { + return indexViewTypeHashMap[position]!! + } +} + +abstract class ProgressPresenter(itemView: View) : RecyclerView.ViewHolder(itemView) + +class ProgressHeaderPresenter(itemView: View) : ProgressPresenter(itemView) { + var repositoryCategory: RepositoryCategory? = null + + init { + val completionRateGraphView = itemView.graph_category_completion_rate_view + val completionRateTabLayout = itemView.graph_category_completion_rate_tablayout + + completionRateGraphView.scrubLineColor = Color.parseColor("#111111") + completionRateGraphView.baseLineColor = Color.WHITE + completionRateGraphView.isScrubEnabled = true + completionRateGraphView.animateChanges = true + + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("1W")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("1M")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("3M")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("6M")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("1Y")) + } + + fun bindView(repositoryCategory: RepositoryCategory) { + this.repositoryCategory = repositoryCategory + + val numberOfCompletedExercises = RepositoryRoutine.getNumberOfCompletedExercises(repositoryCategory.exercises) + val numberOfExercises = RepositoryRoutine.getNumberOfExercises(repositoryCategory.exercises) + val completionRate = RepositoryCategory.getCompletionRate(repositoryCategory) + + itemView.completed_exercises_value.text = "$numberOfCompletedExercises out of $numberOfExercises" + itemView.completion_rate_value.text = "${completionRate.label}" + + val completionRateGraphView = itemView.graph_category_completion_rate_view + val completionRateTabLayout = itemView.graph_category_completion_rate_tablayout + + val completionRateAdapter = CategoryCompletionRateAdapter() + + completionRateGraphView.adapter = completionRateAdapter + completionRateGraphView.setScrubListener { + val dateTimeCompletionRate = it as? CategoryDateTimeCompletionRate + + dateTimeCompletionRate?.let { + itemView.graph_category_completion_rate_title.text = it.dateTime.toString("dd MMMM, YYYY", Locale.ENGLISH) + + if (it.repositoryCategory != null) { + val completionRate = RepositoryCategory.getCompletionRate(it.repositoryCategory) + + itemView.graph_category_completion_rate_description.text = "${completionRate.label}" + } else { + itemView.graph_category_completion_rate_description.text = "Not Completed" + } + } + } + + completionRateTabLayout.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + updateCompletionRateTitle() + + when (tab.position) { + 0 -> updateCompletionRateGraph(completionRateAdapter, 7) + 1 -> updateCompletionRateGraph(completionRateAdapter, 30) + 2 -> updateCompletionRateGraph(completionRateAdapter, 90) + 3 -> updateCompletionRateGraph(completionRateAdapter, 180) + else -> updateCompletionRateGraph(completionRateAdapter, 360) + } + } + + override fun onTabUnselected(tab: TabLayout.Tab) { + + } + + override fun onTabReselected(tab: TabLayout.Tab) { + + } + }) + + updateCompletionRateGraph(completionRateAdapter, 7) + updateCompletionRateTitle() + } + + fun updateCompletionRateTitle() { + repositoryCategory?.let { + val completionRate = RepositoryCategory.getCompletionRate(it) + + itemView.graph_category_completion_rate_title.text = DateTime(it.routine!!.startTime).toString("dd MMMM, YYYY", Locale.ENGLISH) + itemView.graph_category_completion_rate_description.text = "${completionRate.label}" + } + } + + fun updateCompletionRateGraph(adapter: CategoryCompletionRateAdapter, minusDays: Int = 7) { + val start = DateTime.now().withTimeAtStartOfDay().minusDays(minusDays) + val end = DateTime.now() + + Repository.realm.where(RepositoryRoutine::class.java) + .between("startTime", start.toDate(), end.toDate()) + .findAllAsync() + .sort("startTime", Sort.DESCENDING) + .asObservable() + .filter { it.isLoaded } + .map { + val dates = ArrayList() + + for (index in 1..minusDays) { + val date = start.plusDays(index) + + val repositoryCategory: RepositoryCategory? = it.filter { + val startTime = DateTime(it.startTime) + + date.dayOfMonth == startTime.dayOfMonth + && date.monthOfYear == startTime.monthOfYear + && date.year == startTime.year + }.firstOrNull()?.categories?.filter { + it.categoryId == repositoryCategory?.categoryId + }?.firstOrNull() + + if (repositoryCategory != null) { + dates.add(CategoryDateTimeCompletionRate(date, repositoryCategory)) + } else { + dates.add(CategoryDateTimeCompletionRate(date, null)) + } + } + + dates + } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(itemView) + .subscribe { + adapter.changeData(it) + } + } +} + +class ProgressCardPresenter(itemView: View) : ProgressPresenter(itemView) { + fun bindView(repositoryExercise: RepositoryExercise) { + itemView.exercise_title.text = repositoryExercise.title + itemView.exercise_summary.text = LogWorkoutPresenter().getToolbarDescription(repositoryExercise) + + itemView.full_report_button.setOnClickListener { + val intent = Intent(it.context, ProgressExerciseActivity::class.java) + intent.putExtra(Constants.exerciseId, repositoryExercise.exerciseId) + + it.context.startActivity(intent) + } + + itemView.edit_button.setOnClickListener { + UiEvent.showDialog(DialogType.ProgressActivityLogWorkout, repositoryExercise.exerciseId) + } + + itemView.exercise_sets.removeAllViews() + + if (RepositoryExercise.isCompleted(repositoryExercise)) { + itemView.exercise_sets.setVisible() + } else { + itemView.exercise_sets.setGone() + } + + for ((index, repositorySet) in repositoryExercise.sets.withIndex()) { + val view = itemView.exercise_sets.inflate(R.layout.activity_progress_card_set) + + if (repositorySet.isTimed) { + val rawSeconds = repositorySet.seconds + + val stringMinutes = rawSeconds.formatMinutes(format = false) + val numberOfMinutes = rawSeconds.formatMinutesAsNumber() + val stringSeconds = rawSeconds.formatSeconds(format = false) + val numberOfSeconds = rawSeconds.formatSecondsAsNumber() + + val minutes = if (numberOfMinutes == 1) { "Minute" } else { "Minutes" } + val seconds = if (numberOfSeconds == 1) { "Second" } else { "Seconds" } + + if (rawSeconds < 60) { + view.left_value.text = "$stringSeconds $seconds" + } else if (numberOfSeconds == 0 || numberOfSeconds == 60) { + view.left_value.text = "$stringMinutes $minutes" + } else { + view.left_value.text = "$stringMinutes $minutes, $stringSeconds $seconds" + } + + view.left_label.text = "Set ${index + 1}" + + view.right_value.text = "" + view.right_label.text = "" + } else { + val reps = if (repositorySet.reps == 1) { "Rep" } else { "Reps" } + + view.left_value.text = "${repositorySet.reps} $reps" + view.left_label.text = "Set ${index + 1}" + + if (repositorySet.weight > 0.0) { + view.right_value.text = "${repositorySet.weight}" + view.right_label.text = "Weight" + } else { + view.right_value.text = "" + view.right_label.text = "" + } + } + + itemView.exercise_sets.addView(view) + } + } +} + +class ProgressTitlePresenter(itemView: View) : ProgressPresenter(itemView) { + fun bindView(repositorySection: RepositorySection) { + if (layoutPosition == 0) { + itemView.title.setPadding( + itemView.title.paddingLeft, + itemView.title.paddingLeft, + itemView.title.paddingRight, + itemView.title.paddingBottom) + } else { + itemView.title.setPadding( + itemView.title.paddingLeft, + itemView.title.paddingBottom, + itemView.title.paddingRight, + itemView.title.paddingBottom) + } + + itemView.title.text = repositorySection.title + } +} diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressPagerAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressPagerAdapter.kt index 42c4c40f..442c9151 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressPagerAdapter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ProgressPagerAdapter.kt @@ -1,6 +1,5 @@ package com.bodyweight.fitness.adapter -import android.graphics.Color import android.support.v4.view.PagerAdapter import android.support.v4.view.ViewPager @@ -9,49 +8,30 @@ import android.support.v7.widget.RecyclerView import android.view.View import android.view.ViewGroup -import com.hookedonplay.decoviewlib.charts.DecoDrawEffect -import com.hookedonplay.decoviewlib.charts.SeriesItem -import com.hookedonplay.decoviewlib.events.DecoEvent - -import org.joda.time.DateTime -import org.joda.time.Duration -import org.joda.time.format.PeriodFormatterBuilder - import com.bodyweight.fitness.R -import com.bodyweight.fitness.Exercise import com.bodyweight.fitness.inflate -import com.bodyweight.fitness.model.repository.RepositoryRoutine +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.view.progress.ProgressGeneralView +import com.bodyweight.fitness.view.progress.ProgressGeneralViewPresenter -import kotlinx.android.synthetic.main.activity_progress_info.view.* import kotlinx.android.synthetic.main.activity_progress_page.view.* import java.util.* class ProgressPagerAdapter(private val repositoryRoutine: RepositoryRoutine) : PagerAdapter() { - private var numberOfExercises: Int = 0 - private var currentExerciseIndex: Int = 0 private val viewWeakHashMap = WeakHashMap() - - init { - for (repositoryExercise in repositoryRoutine.exercises) { - if (repositoryExercise.isVisible) { - numberOfExercises++ - - if (Exercise.isCompleted(repositoryExercise)) { - currentExerciseIndex++ - } - } - } - } + private val adapterList = ArrayList() override fun instantiateItem(parent: ViewGroup, position: Int): Any { val viewPager = parent as ViewPager if (position == 0) { - val view = parent.inflate(R.layout.activity_progress_info) + val view: ProgressGeneralView = parent.inflate(R.layout.activity_progress_general) as ProgressGeneralView + val presenter: ProgressGeneralViewPresenter = view.presenter as ProgressGeneralViewPresenter - createStartTimeEndTime(view) - createDecoView(view) + presenter.repositoryRoutine = repositoryRoutine + + view.updateView() viewPager.addView(view) return view @@ -82,7 +62,7 @@ class ProgressPagerAdapter(private val repositoryRoutine: RepositoryRoutine) : P override fun getPageTitle(position: Int): CharSequence { when (position) { 0 -> return "General" - else -> return repositoryRoutine.categories[position - 1].title + else -> return repositoryRoutine.categories[position - 1].title!! } } @@ -90,98 +70,20 @@ class ProgressPagerAdapter(private val repositoryRoutine: RepositoryRoutine) : P viewWeakHashMap[tabPosition]?.smoothScrollToPosition(0) } - private fun createStartTimeEndTime(view: View) { - val startTime = DateTime(repositoryRoutine.startTime) - val lastUpdatedTime = DateTime(repositoryRoutine.lastUpdatedTime) - val duration = Duration(startTime, lastUpdatedTime) - - view.start_time_text.text = startTime.toString("HH:mm", Locale.ENGLISH) - view.end_time_text.text = lastUpdatedTime.toString("HH:mm", Locale.ENGLISH) - - if (duration.toStandardMinutes().minutes < 10) { - view.workout_length_text.text = "--" - } else { - view.workout_length_text.text = PeriodFormatterBuilder() - .appendHours() - .appendSuffix("h ") - .appendMinutes() - .appendSuffix("m") - .toFormatter() - .print(duration.toPeriod()) - } - - if (currentExerciseIndex == numberOfExercises) { - view.end_time_label.text = "End Time" - } else { - view.end_time_label.text = "Last Updated" + fun onRepositoryUpdated() { + for (adapter in adapterList) { + adapter.notifyDataSetChanged() } } private fun createRecyclerView(view: View, position: Int) { val repositoryCategory = repositoryRoutine.categories[position - 1] + val adapter = ProgressListAdapter(repositoryCategory) view.recycler_view.layoutManager = LinearLayoutManager(view.context) - view.recycler_view.adapter = ProgressAdapter(repositoryCategory) + view.recycler_view.adapter = adapter viewWeakHashMap.put(position, view.recycler_view) - } - - private fun createDecoView(view: View) { - val backIndex = view.dynamicArcView.addSeries(SeriesItem.Builder(Color.parseColor("#FFE2E2E2")) - .setRange(0f, numberOfExercises.toFloat(), 0f) - .setInitialVisibility(true) - .build()) - - val frontSeries = SeriesItem.Builder(Color.parseColor("#0B9394")) - .setRange(0f, numberOfExercises.toFloat(), 0f) - .setInitialVisibility(false) - .build() - - frontSeries.addArcSeriesItemListener(object : SeriesItem.SeriesItemListener { - override fun onSeriesItemAnimationProgress(percentComplete: Float, currentPosition: Float) { - val percentFilled = (currentPosition - frontSeries.minValue) / (frontSeries.maxValue - frontSeries.minValue) - - view.textPercentage.text = String.format("%.0f%%", percentFilled * 100f) - } - - override fun onSeriesItemDisplayProgress(percentComplete: Float) { } - }) - - frontSeries.addArcSeriesItemListener(object : SeriesItem.SeriesItemListener { - override fun onSeriesItemAnimationProgress(percentComplete: Float, currentPosition: Float) { - val remainingExercises = (frontSeries.maxValue - currentPosition).toInt() - - if (remainingExercises > 0) { - view.textRemaining.text = String.format("%d exercises to go", remainingExercises) - } else { - view.textRemaining.text = "Congratulations!" - } - } - - override fun onSeriesItemDisplayProgress(percentComplete: Float) { } - }) - - val frontIndex = view.dynamicArcView.addSeries(frontSeries) - - view.dynamicArcView.executeReset() - view.dynamicArcView.addEvent(DecoEvent.Builder(numberOfExercises.toFloat()) - .setIndex(backIndex) - .setDuration(0) - .setDelay(0) - .build()) - - view.dynamicArcView.addEvent(DecoEvent.Builder(DecoDrawEffect.EffectType.EFFECT_SPIRAL_OUT) - .setIndex(frontIndex) - .setDuration(0) - .setDelay(0) - .build()) - - view.dynamicArcView.addEvent(DecoEvent.Builder(currentExerciseIndex.toFloat()) - .setIndex(frontIndex) - .setDelay(0) - .build()) - - view.textPercentage.text = "" - view.textRemaining.text = "" + adapterList.add(adapter) } } diff --git a/app/src/main/kotlin/com/bodyweight/fitness/adapter/ToolbarSpinnerAdapter.kt b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ToolbarSpinnerAdapter.kt new file mode 100644 index 00000000..f06bc6ac --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/adapter/ToolbarSpinnerAdapter.kt @@ -0,0 +1,69 @@ +package com.bodyweight.fitness.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView + +import com.bodyweight.fitness.R +import com.bodyweight.fitness.stream.RoutineStream + +data class SpinnerRoutine(val id: Int, val title: String, val subtitle: String) + +class ToolbarSpinnerAdapter : BaseAdapter() { + val routines: List + + init { + if (RoutineStream.routine.routineId == "routine0") { + routines = listOf( + SpinnerRoutine(0, "Bodyweight Fitness", "Recommended Routine"), + SpinnerRoutine(1, "Molding Mobility", "Flexibility Routine")) + } else { + routines = listOf( + SpinnerRoutine(1, "Molding Mobility", "Flexibility Routine"), + SpinnerRoutine(0, "Bodyweight Fitness", "Recommended Routine")) + } + } + + override fun getCount(): Int { + return routines.size + } + + override fun getItem(position: Int): Any { + return routines[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getDropDownView(position: Int, view: View?, parent: ViewGroup): View { + var view = view + if (view == null || view.tag.toString() != "DROPDOWN") { + + view = LayoutInflater.from(parent.context).inflate(R.layout.toolbar_spinner_item_dropdown, parent, false) + view.tag = "DROPDOWN" + } + + val textView = view!!.findViewById(android.R.id.text1) as TextView + textView.text = getTitle(position) + + return view + } + + override fun getView(position: Int, view: View?, parent: ViewGroup): View { + var view = view + + if (view == null || view.tag.toString() != "NON_DROPDOWN") { + view = LayoutInflater.from(parent.context).inflate(R.layout.toolbar_spinner_item_actionbar, parent, false) + view.tag = "NON_DROPDOWN" + } + + return view!! + } + + private fun getTitle(position: Int): String { + return routines[position].title + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/dialog/LogWorkoutDialog.kt b/app/src/main/kotlin/com/bodyweight/fitness/dialog/LogWorkoutDialog.kt new file mode 100644 index 00000000..03eede00 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/dialog/LogWorkoutDialog.kt @@ -0,0 +1,677 @@ +package com.bodyweight.fitness.dialog + +import android.app.Dialog +import android.content.DialogInterface +import android.support.design.widget.BottomSheetBehavior +import android.support.design.widget.BottomSheetDialogFragment +import android.support.design.widget.CoordinatorLayout +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView + +import com.bodyweight.fitness.* +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.stream.Stream +import com.bodyweight.fitness.utils.Preferences +import com.bodyweight.fitness.view.listener.RepeatListener + +import kotlinx.android.synthetic.main.view_dialog_log_workout.view.* + +import java.util.* + +import kotlin.properties.Delegates + +class LogWorkoutPresenter { + fun getPreviousWorkoutDescription(repositoryExercise: RepositoryExercise): String { + if (!RepositoryExercise.isCompleted(repositoryExercise)) { + return "Not Completed" + } + + val numberOfSets = repositoryExercise.sets.size + val numberOfReps = repositoryExercise.sets.map { it.reps }.sum() + + val rawSeconds = repositoryExercise.sets.map { it.seconds }.sum() + + val stringMinutes = rawSeconds.formatMinutes(format = false) + val numberOfMinutes = rawSeconds.formatMinutesAsNumber() + val stringSeconds = rawSeconds.formatSeconds(format = false) + val numberOfSeconds = rawSeconds.formatSecondsAsNumber() + + val sets = if (numberOfSets == 1) { "Set" } else { "Sets" } + val reps = if (numberOfReps == 1) { "Rep" } else { "Reps" } + + val minutes = if (numberOfMinutes == 1) { "Minute" } else { "Minutes" } + val seconds = if (numberOfSeconds == 1) { "Second" } else { "Seconds" } + + if (numberOfSets == 1) { + if (repositoryExercise.defaultSet == "timed") { + if (rawSeconds < 60) { + return "$numberOfSets $sets, $stringSeconds $seconds" + } else if (numberOfSeconds == 60) { + return "$numberOfSets $sets, $stringMinutes $minutes" + } else if (numberOfSeconds == 0) { + return "$numberOfSets $sets, $stringMinutes $minutes" + } else { + return "$numberOfSets $sets, $stringMinutes $minutes $stringSeconds $seconds" + } + } + + return "1 Set, $numberOfReps $reps" + } else { + if (repositoryExercise.defaultSet == "timed") { + var description = "" + + for (set in repositoryExercise.sets) { + if (repositoryExercise.sets.last() == set) { + description += "${set.seconds}s" + } else { + description += "${set.seconds}s-" + } + } + + return description + } + + var description = "" + + for (set in repositoryExercise.sets) { + if (repositoryExercise.sets.last() == set) { + description += "${set.reps}" + } else { + description += "${set.reps}-" + } + } + + return description + } + } + + fun getToolbarDescription(repositoryExercise: RepositoryExercise): String { + if (!RepositoryExercise.isCompleted(repositoryExercise)) { + return "Not Completed" + } + + val numberOfSets = repositoryExercise.sets.size + val numberOfReps = repositoryExercise.sets.map { it.reps }.sum() + + val rawSeconds = repositoryExercise.sets.map { it.seconds }.sum() + + val stringMinutes = rawSeconds.formatMinutes(format = false) + val numberOfMinutes = rawSeconds.formatMinutesAsNumber() + val stringSeconds = rawSeconds.formatSeconds(format = false) + val numberOfSeconds = rawSeconds.formatSecondsAsNumber() + + val sets = if (numberOfSets == 1) { "Set" } else { "Sets" } + val reps = if (numberOfReps == 1) { "Rep" } else { "Reps" } + + val minutes = if (numberOfMinutes == 1) { "Minute" } else { "Minutes" } + val seconds = if (numberOfSeconds == 1) { "Second" } else { "Seconds" } + + if (repositoryExercise.defaultSet == "timed") { + if (rawSeconds < 60) { + return "$numberOfSets $sets, $stringSeconds $seconds" + } else if (numberOfSeconds == 0) { + return "$numberOfSets $sets, $stringMinutes $minutes" + } + + return "$numberOfSets $sets, $stringMinutes $minutes, $stringSeconds $seconds" + } + + return "$numberOfSets $sets, $numberOfReps $reps" + } +} + +class LogWorkoutDialog : BottomSheetDialogFragment() { + private val initialInterval = 400 + private val normalInterval = 100 + + private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + dismiss() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + } + + private val logWorkoutPresenter: LogWorkoutPresenter = LogWorkoutPresenter() + private val repositoryRoutine: RepositoryRoutine by lazy { + val primaryKeyRoutineId = arguments.getString(Constants.primaryKeyRoutineId) + + if (primaryKeyRoutineId == null) { + Repository.repositoryRoutineForToday + } else { + Repository.getRepositoryRoutineForPrimaryKeyRoutineId(primaryKeyRoutineId) + } + } + + private val repositoryExercise: RepositoryExercise by lazy { + val exerciseId = arguments.getString(Constants.exerciseId) + + repositoryRoutine.exercises.filter { it.exerciseId.equals(exerciseId) }.first() + } + + private var repositorySet: RepositorySet by Delegates.notNull() + + private var layout: View by Delegates.notNull() + private var rowLayout: LinearLayout by Delegates.notNull() + private val viewSets: ArrayList = ArrayList() + + override fun setupDialog(dialog: Dialog?, style: Int) { + super.setupDialog(dialog, style) + + layout = View.inflate(context, R.layout.view_dialog_log_workout, null) + dialog?.setContentView(layout) + + val params = ((layout.parent as View).layoutParams as CoordinatorLayout.LayoutParams) + val behavior = params.behavior + + if (behavior is BottomSheetBehavior) { + behavior.peekHeight = 400.toPx(context) + behavior.setBottomSheetCallback(bottomSheetCallback) + } + + layout.toolbar.title = repositoryExercise.title + layout.toolbar.subtitle = LogWorkoutPresenter().getToolbarDescription(repositoryExercise) + + layout.repsIncrease.setOnTouchListener(RepeatListener(initialInterval, normalInterval, { + increaseLeft() + })); + + layout.repsDecrease.setOnTouchListener(RepeatListener(initialInterval, normalInterval, { + decreaseLeft() + })); + + layout.weightIncrease.setOnTouchListener(RepeatListener(initialInterval, normalInterval, { + increaseRight() + })); + + layout.weightDecrease.setOnTouchListener(RepeatListener(initialInterval, normalInterval, { + decreaseRight() + })); + + val realm = Repository.realm + val results = realm.where(RepositoryRoutine::class.java) + .lessThan("startTime", repositoryRoutine.startTime) + .notEqualTo("id", repositoryRoutine.id) + .findAll() + + if (results.isNotEmpty()) { + layout.previous_workout_label.setVisible() + layout.previous_workout_value.setVisible() + layout.this_workout_label.setVisible() + + results.lastOrNull()?.let { + it.exercises.filter { + it.exerciseId.equals(repositoryExercise.exerciseId) + }.firstOrNull()?.let { + layout.previous_workout_value.text = logWorkoutPresenter.getPreviousWorkoutDescription(it) + } + } + } else { + layout.previous_workout_label.setGone() + layout.previous_workout_value.setGone() + layout.this_workout_label.setGone() + } + + layout.actionView.setGone() + layout.saveButton.setOnClickListener { dismiss() } + + inflateToolbarMenu() + buildSets() + } + + override fun onDismiss(dialog: DialogInterface?) { + super.onDismiss(dialog) + + val mode = repositoryExercise.section!!.mode + if (mode.equals(SectionMode.Levels.asString) || mode.equals(SectionMode.Pick.asString)) { + Repository.realm.executeTransaction { + repositoryExercise.visible = RepositoryExercise.isCompleted(repositoryExercise) + } + } + + Stream.setRepository() + } + + fun updateToolbarMenu() { + layout.toolbar.subtitle = logWorkoutPresenter.getToolbarDescription(repositoryExercise) + + val menu = layout.toolbar.menu + + if(viewSets.size >= Constants.maximumNumberOfSets) { + menu.findItem(R.id.action_add_set).isVisible = false + menu.findItem(R.id.action_add_timed_set).isVisible = false + } else if (viewSets.size == 1) { + if (repositoryExercise.defaultSet.equals("timed")) { + menu.findItem(R.id.action_add_set).isVisible = false + menu.findItem(R.id.action_add_timed_set).isVisible = true + } else { + menu.findItem(R.id.action_add_set).isVisible = true + menu.findItem(R.id.action_add_timed_set).isVisible = false + } + + menu.findItem(R.id.action_remove_last_set).isVisible = false + } else { + if (repositoryExercise.defaultSet.equals("timed")) { + menu.findItem(R.id.action_add_set).isVisible = false + menu.findItem(R.id.action_add_timed_set).isVisible = true + } else { + menu.findItem(R.id.action_add_set).isVisible = true + menu.findItem(R.id.action_add_timed_set).isVisible = false + } + + menu.findItem(R.id.action_remove_last_set).isVisible = true + } + } + + fun inflateToolbarMenu() { + layout.toolbar.inflateMenu(R.menu.menu_dialog_log_workout) + layout.toolbar.setOnMenuItemClickListener { + when (it.itemId) { + R.id.action_add_set -> { + createSet() + + true + } + R.id.action_add_timed_set -> { + createSet(isTimed = true) + + true + } + R.id.action_remove_last_set -> { + if (shouldRemoveSet()) { + removeLastSet() + updateToolbarMenu() + } + + true + } + else -> false + } + } + + updateToolbarMenu() + } + + fun invalidateToolbarMenu() { + layout.toolbar.menu.clear() + } + + fun createSet(isTimed: Boolean = false) { + if (shouldAddSet()) { + val lastSet = repositoryExercise.sets.last() + var set: RepositorySet? = null + + Repository.realm.executeTransaction { + set = Repository.realm.createObject(RepositorySet::class.java) + + set?.let { + it.id = "Set-" + UUID.randomUUID().toString() + it.isTimed = isTimed + it.seconds = lastSet.seconds + it.weight = lastSet.weight + it.reps = lastSet.reps + it.exercise = repositoryExercise + } + } + + set?.let { + addSet(it, isTimed) + } + + updateToolbarMenu() + } + } + + fun buildSets() { + for (set in repositoryExercise.sets) { + if (shouldAddSet()) { + addSet(set, set.isTimed) + } + } + + updateToolbarMenu() + } + + fun actionViewOpened() { + invalidateToolbarMenu() + + layout.saveButton.text = "Back" + layout.saveButton.setOnClickListener { + updateSets() + + layout.toolbar.subtitle = logWorkoutPresenter.getToolbarDescription(repositoryExercise) + + layout.setView.setVisible() + layout.actionView.setGone() + + actionViewClosed() + } + } + + fun actionViewClosed() { + inflateToolbarMenu() + + layout.saveButton.text = "Save" + layout.saveButton.setOnClickListener { + dismiss() + } + } + + fun updateSets() { + for ((index, view) in viewSets.toArray().withIndex()) { + val set = repositoryExercise.sets[index] + + updateSet(set, view as View, set.isTimed) + } + } + + fun shouldAddSet(): Boolean { + return viewSets.size < Constants.maximumNumberOfSets + } + + fun shouldRemoveSet(): Boolean { + return viewSets.size != 1 + } + + fun addRow() { + if (listOf(0, 3, 6, 9).contains(viewSets.size)) { + val view = layout.inflate(R.layout.view_dialog_log_workout_row, layout.setView) + + rowLayout = view as LinearLayout + layout.setView.addView(view) + } + } + + fun addSet(set: RepositorySet, isTimed: Boolean = false) { + addRow() + + val view = if (isTimed) { + layout.inflate(R.layout.view_dialog_log_workout_timed_set, layout.setView) + } else { + layout.inflate(R.layout.view_dialog_log_workout_set, layout.setView) + } + + updateSet(set, view, isTimed) + + view.setOnClickListener { + val index = repositoryExercise.sets.indexOf(set) + + updateActionView(set, index + 1, isTimed) + + layout.setView.visibility = View.GONE + layout.actionView.visibility = View.VISIBLE + + actionViewOpened() + } + + rowLayout.addView(view) + + Repository.realm.executeTransaction { + if (!repositoryExercise.sets.contains(set)) { + repositoryExercise.sets.add(set) + } + } + + viewSets.add(view) + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun removeLastSet() { + Repository.realm.executeTransaction { + repositoryExercise.sets.remove(repositoryExercise.sets.last()) + } + + viewSets.remove(viewSets.last()) + rowLayout.removeViewAt(rowLayout.childCount - 1) + + if (rowLayout.childCount == 0) { + layout.setView.removeView(rowLayout) + rowLayout = layout.setView.getChildAt(layout.setView.childCount - 1) as LinearLayout + } + } + + fun updateActionView(repositorySet: RepositorySet, index: Int, isTimed: Boolean = false) { + if (isTimed) { + this.repositorySet = repositorySet + + layout.setValue.text = index.toString() + + layout.repsValue.text = repositorySet.seconds.formatMinutes(false) + layout.repsDescription.text = "Minutes" + + layout.weightValue.text = repositorySet.seconds.formatSeconds(false) + layout.weightDescription.text = "Seconds" + } else { + this.repositorySet = repositorySet + + layout.setValue.text = index.toString() + + layout.repsValue.text = repositorySet.reps.toString() + layout.repsDescription.text = "Reps" + + layout.weightValue.text = repositorySet.weight.toString() + layout.weightDescription.text = "Weight (${Preferences.weightMeasurementUnit.asString})" + } + } + + fun updateSet(repositorySet: RepositorySet, view: View, isTimed: Boolean = false) { + if (isTimed) { + val secondsOnlyValue: TextView = view.findViewById(R.id.secondsOnlyValue) as TextView + val minutes: TextView = view.findViewById(R.id.minutesValue) as TextView + val seconds: TextView = view.findViewById(R.id.secondsValue) as TextView + val center: View = view.findViewById(R.id.center) + + if (repositorySet.seconds < 60) { + secondsOnlyValue.setVisible() + + minutes.setGone() + seconds.setGone() + center.setGone() + + secondsOnlyValue.text = repositorySet.seconds.formatSecondsPostfix() + } else if (repositorySet.seconds >= 60 && repositorySet.seconds.formatSecondsAsNumber() == 0) { + secondsOnlyValue.setVisible() + + minutes.setGone() + seconds.setGone() + center.setGone() + + secondsOnlyValue.text = repositorySet.seconds.formatMinutesPostfix() + } else { + secondsOnlyValue.setGone() + + minutes.setVisible() + seconds.setVisible() + center.setVisible() + + minutes.text = repositorySet.seconds.formatMinutesPostfix() + seconds.text = repositorySet.seconds.formatSecondsPostfix() + } + } else { + val repsOnlyValue: TextView = view.findViewById(R.id.repsOnlyValue) as TextView + val reps: TextView = view.findViewById(R.id.repsValue) as TextView + val weight: TextView = view.findViewById(R.id.weightValue) as TextView + val center: View = view.findViewById(R.id.center) + + if (repositorySet.weight == 0.0) { + repsOnlyValue.setVisible() + + reps.setGone() + weight.setGone() + center.setGone() + + repsOnlyValue.text = repositorySet.reps.formatReps() + } else { + repsOnlyValue.setGone() + + reps.setVisible() + weight.setVisible() + center.setVisible() + + reps.text = repositorySet.reps.formatReps(true) + weight.text = repositorySet.weight.formatWeight() + } + } + } + + fun increaseLeft() { + if (repositorySet.isTimed) { + increaseMinutes() + } else { + increaseReps() + } + } + + fun decreaseLeft() { + if (repositorySet.isTimed) { + decreaseMinutes() + } else { + decreaseReps() + } + } + + fun increaseRight() { + if (repositorySet.isTimed) { + increaseSeconds() + } else { + increaseWeight() + } + } + + fun decreaseRight() { + if (repositorySet.isTimed) { + decreaseSeconds() + } else { + decreaseWeight() + } + } + + fun increaseMinutes() { + if (repositorySet.seconds / 60 >= 5) { + return + } + + Repository.realm.executeTransaction { + repositorySet.seconds += 60 + } + + layout.repsValue.text = repositorySet.seconds.formatMinutes(false) + layout.weightValue.text = repositorySet.seconds.formatSeconds(false) + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun decreaseMinutes() { + if (repositorySet.seconds < 60) { + return + } + + Repository.realm.executeTransaction { + repositorySet.seconds -= 60 + } + + layout.repsValue.text = repositorySet.seconds.formatMinutes(false) + layout.weightValue.text = repositorySet.seconds.formatSeconds(false) + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun increaseSeconds() { + Repository.realm.executeTransaction { + if (repositorySet.seconds % 60 == 59) { + repositorySet.seconds -= 59 + } else { + repositorySet.seconds += 1 + } + } + + layout.repsValue.text = repositorySet.seconds.formatMinutes(false) + layout.weightValue.text = repositorySet.seconds.formatSeconds(false) + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun decreaseSeconds() { + Repository.realm.executeTransaction { + if (repositorySet.seconds % 60 == 0) { + repositorySet.seconds += 59 + } else { + repositorySet.seconds -= 1 + } + } + + layout.repsValue.text = repositorySet.seconds.formatMinutes(false) + layout.weightValue.text = repositorySet.seconds.formatSeconds(false) + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun increaseReps() { + if (repositorySet.reps >= 50) { + return + } + + Repository.realm.executeTransaction { + repositorySet.reps += 1 + } + + layout.repsValue.text = repositorySet.reps.toString() + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun decreaseReps() { + if (repositorySet.reps == 0) { + return + } + + Repository.realm.executeTransaction { + repositorySet.reps -= 1 + } + + layout.repsValue.text = repositorySet.reps.toString() + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun increaseWeight() { + if (repositorySet.weight >= 250.0) { + return + } + + Repository.realm.executeTransaction { + if (Preferences.weightMeasurementUnit.equals(WeightMeasurementUnit.Kg)) { + repositorySet.weight += 0.5 + } else { + repositorySet.weight += 1.0 + } + } + + layout.weightValue.text = repositorySet.weight.toString() + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } + + fun decreaseWeight() { + if (repositorySet.weight <= 0) { + return + } + + Repository.realm.executeTransaction { + if (Preferences.weightMeasurementUnit.equals(WeightMeasurementUnit.Kg)) { + repositorySet.weight -= 0.5 + } else { + repositorySet.weight -= 1.0 + } + } + + layout.weightValue.text = repositorySet.weight.toString() + + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = false) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/dialog/ProgressDialog.kt b/app/src/main/kotlin/com/bodyweight/fitness/dialog/ProgressDialog.kt new file mode 100644 index 00000000..a983b9c8 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/dialog/ProgressDialog.kt @@ -0,0 +1,113 @@ +package com.bodyweight.fitness.dialog + +import android.app.Dialog +import android.support.design.widget.BottomSheetBehavior +import android.support.design.widget.BottomSheetDialogFragment +import android.support.design.widget.CoordinatorLayout +import android.view.View + +import com.bodyweight.fitness.* +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.stream.RoutineStream + +import kotlinx.android.synthetic.main.view_dialog_progress.view.* + +import kotlin.properties.Delegates + +class ProgressDialog : BottomSheetDialogFragment() { + private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + dismiss() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + } + + private val exercise: Exercise = RoutineStream.exercise + private val availableLevels: Int = exercise.section!!.availableLevels + private var chosenLevel: Int = exercise.section!!.currentLevel + private var layout: View by Delegates.notNull() + + override fun setupDialog(dialog: Dialog?, style: Int) { + super.setupDialog(dialog, style) + + layout = View.inflate(context, R.layout.view_dialog_progress, null) + dialog?.setContentView(layout) + + val params = ((layout.parent as View).layoutParams as CoordinatorLayout.LayoutParams) + val behavior = params.behavior + + if (behavior is BottomSheetBehavior) { + behavior.peekHeight = 400.toPx(context) + behavior.setBottomSheetCallback(bottomSheetCallback) + } + + layout.level_previous_button.setOnClickListener { + chosenLevel -= 1 + updateDialog(); + } + + layout.level_next_button.setOnClickListener { + chosenLevel += 1 + updateDialog(); + } + + layout.level_confirm_button.setOnClickListener { + val chosenExercise = exercise.section!!.exercises[chosenLevel] + + RoutineStream.setLevel(chosenExercise, chosenLevel) + + if (Repository.repositoryRoutineForTodayExists()) { + val repositoryRoutine = Repository.repositoryRoutineForToday + + Repository.realm.executeTransaction { + repositoryRoutine.exercises.filter { it.exerciseId == exercise.exerciseId }.firstOrNull()?.let { + it.visible == false + } + + repositoryRoutine.exercises.filter { it.exerciseId == chosenExercise.exerciseId }.firstOrNull()?.let { + it.visible == true + } + } + } + + dialog?.dismiss() + } + + layout.level_progress_bar.setWheelSize(12) + layout.level_progress_bar.setProgressColor(primary()) + layout.level_progress_bar.setProgressBackgroundColor(primaryDark()); + + updateDialog() + } + + fun updateDialog() { + val chosenExercise = exercise.section!!.exercises[chosenLevel] + + layout.toolbar.title = chosenExercise.title + layout.toolbar.subtitle = chosenExercise.description + + if (exercise.section!!.sectionMode == SectionMode.Levels) { + layout.level_text_view.text = chosenExercise.level + } else { + layout.level_text_view.text = "Pick One" + } + + if (chosenLevel == 0) { + layout.level_previous_button.setInvisible() + } else { + layout.level_previous_button.setVisible() + } + + if (chosenLevel >= (availableLevels - 1)) { + layout.level_next_button.setInvisible() + } else { + layout.level_next_button.setVisible() + } + + layout.level_progress_bar.setProgress((1f / availableLevels) * (chosenLevel + 1)); + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/model/CalendarDay.kt b/app/src/main/kotlin/com/bodyweight/fitness/model/CalendarDay.kt deleted file mode 100644 index be3c1d51..00000000 --- a/app/src/main/kotlin/com/bodyweight/fitness/model/CalendarDay.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.bodyweight.fitness.model - -import com.bodyweight.fitness.adapter.CalendarAdapter -import org.joda.time.DateTime - -data class CalendarDay(var page: Int = 60, var day: Int = 3) { - fun getDate(): DateTime { - if (this.page == CalendarAdapter.DEFAULT_POSITION) { - return DateTime() - .dayOfWeek() - .withMinimumValue() - .plusDays(this.day) - } else if (this.page < CalendarAdapter.DEFAULT_POSITION) { - return DateTime() - .minusWeeks(CalendarAdapter.DEFAULT_POSITION - this.page) - .dayOfWeek() - .withMinimumValue() - .plusDays(this.day) - } - - return DateTime() - .plusWeeks(this.page - CalendarAdapter.DEFAULT_POSITION) - .dayOfWeek() - .withMinimumValue() - .plusDays(this.day) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/model/Enum.kt b/app/src/main/kotlin/com/bodyweight/fitness/model/Enum.kt new file mode 100644 index 00000000..02bfa322 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/model/Enum.kt @@ -0,0 +1,57 @@ +package com.bodyweight.fitness.model + +import com.bodyweight.fitness.adapter.CalendarPagerAdapter +import org.joda.time.DateTime +import java.io.Serializable + +data class CalendarDay(var page: Int = 60, var day: Int = 3) { + fun getDate(): DateTime { + if (this.page == CalendarPagerAdapter.DEFAULT_POSITION) { + return DateTime() + .dayOfWeek() + .withMinimumValue() + .plusDays(this.day) + } else if (this.page < CalendarPagerAdapter.DEFAULT_POSITION) { + return DateTime() + .minusWeeks(CalendarPagerAdapter.DEFAULT_POSITION - this.page) + .dayOfWeek() + .withMinimumValue() + .plusDays(this.day) + } + + return DateTime() + .plusWeeks(this.page - CalendarPagerAdapter.DEFAULT_POSITION) + .dayOfWeek() + .withMinimumValue() + .plusDays(this.day) + } +} + +data class DateTimeRepositorySet(val dateTime: DateTime, val repositorySet: RepositorySet? = null) + +data class DateTimeWorkoutLength(val dateTime: DateTime, val repositoryRoutine: RepositoryRoutine? = null) +data class DateTimeCompletionRate(val dateTime: DateTime, val repositoryRoutine: RepositoryRoutine? = null) +data class CategoryDateTimeCompletionRate(val dateTime: DateTime, val repositoryCategory: RepositoryCategory? = null) + +data class CompletionRate(val percentage: Int, val label: String) + +data class SetReps(val set: Int, val reps: Int) +data class Dialog(val dialogType: DialogType, val exerciseId: String) + +enum class DialogType { MainActivityLogWorkout, ProgressActivityLogWorkout, Progress } +enum class WorkoutViewType { + Timer, + RepsLogger +} + +enum class WeightMeasurementUnit constructor(val asString: String) { + Kg("kg"), Lbs("lbs") +} + +enum class RoutineType : Serializable { + Category, Section, Exercise, ExerciseActive; +} + +enum class SectionMode(val asString: String) { + All("all"), Pick("pick"), Levels("levels") +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/model/Routine.kt b/app/src/main/kotlin/com/bodyweight/fitness/model/Routine.kt new file mode 100644 index 00000000..91cd732b --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/model/Routine.kt @@ -0,0 +1,249 @@ +package com.bodyweight.fitness.model + +import com.bodyweight.fitness.persistence.Glacier +import java.io.Serializable +import java.util.* + +abstract class LinkedRoutine : Serializable { + abstract val title: String + abstract val type: RoutineType +} + +class Routine(JSONRoutine: JSONRoutine) : Serializable { + var routineId: String = "routine0" + var title: String = "" + var subtitle: String = "" + + val categories = ArrayList() + val sections = ArrayList
() + val exercises = ArrayList() + val linkedExercises = ArrayList() + val linkedRoutine = ArrayList() + + init { + routineId = JSONRoutine.routineId + title = JSONRoutine.title + subtitle = JSONRoutine.subtitle + + var currentCategory: Category? = null + var currentSection: Section? = null + var currentExercise: Exercise? = null + + for (JSONLinkedRoutine in JSONRoutine.routine) { + if (JSONLinkedRoutine.routineType === RoutineType.Category) { + currentCategory = Category(JSONLinkedRoutine.categoryId, JSONLinkedRoutine.title) + + categories.add(currentCategory) + linkedRoutine.add(currentCategory) + } else if (JSONLinkedRoutine.routineType === RoutineType.Section) { + currentSection = Section( + JSONLinkedRoutine.sectionId, + JSONLinkedRoutine.title, + JSONLinkedRoutine.description, + JSONLinkedRoutine.sectionMode) + + currentCategory!!.insertSection(currentSection) + + sections.add(currentSection) + linkedRoutine.add(currentSection) + } else if (JSONLinkedRoutine.routineType === RoutineType.Exercise) { + val exercise = Exercise( + JSONLinkedRoutine.exerciseId, + JSONLinkedRoutine.level, + JSONLinkedRoutine.title, + JSONLinkedRoutine.description, + JSONLinkedRoutine.youTubeId, + JSONLinkedRoutine.videoId, + JSONLinkedRoutine.defaultSet) + + currentSection!!.insertExercise(exercise) + + exercises.add(exercise) + + if (currentSection.sectionMode === SectionMode.Levels || currentSection.sectionMode === SectionMode.Pick) { + val currentExerciseId = Glacier.get(currentSection.sectionId, String::class.java) + + if (currentExerciseId != null) { + if (exercise.exerciseId.matches(currentExerciseId.toRegex())) { + linkedExercises.add(exercise) + linkedRoutine.add(exercise) + + exercise.previous = currentExercise + + if (currentExercise != null) { + currentExercise.next = exercise + } + + currentExercise = exercise + currentSection.setCurrentLevel(exercise) + } + } else { + if (currentSection.exercises.size == 1) { + linkedExercises.add(exercise) + linkedRoutine.add(exercise) + + exercise.previous = currentExercise + + if (currentExercise != null) { + currentExercise.next = exercise + } + + currentExercise = exercise + } + } + } else { + exercise.previous = currentExercise + + if (currentExercise != null) { + currentExercise.next = exercise + } + + currentExercise = exercise + + linkedExercises.add(exercise) + linkedRoutine.add(exercise) + } + } + } + } + + fun setLevel(exercise: Exercise, level: Int) { + val currentSectionExercise = exercise.section!!.currentExercise + + if (currentSectionExercise != exercise) { + if (currentSectionExercise.previous != null) { + currentSectionExercise.previous!!.next = exercise + } + + if (currentSectionExercise.next != null) { + currentSectionExercise.next!!.previous = exercise + } + + exercise.previous = currentSectionExercise.previous + exercise.next = currentSectionExercise.next + + exercise.section!!.currentLevel = level + + currentSectionExercise.previous = null + currentSectionExercise.next = null + + val indexOfCurrentExercise = linkedRoutine.indexOf(currentSectionExercise) + if (indexOfCurrentExercise > -1) { + linkedRoutine[indexOfCurrentExercise] = exercise + } + + val indexOfLinkedExercise = linkedExercises.indexOf(currentSectionExercise) + if (indexOfLinkedExercise > -1) { + linkedExercises[indexOfLinkedExercise] = exercise + } + } + } +} + +class Category( + val categoryId: String, + val categoryTitle: String +) : LinkedRoutine(), Serializable { + val sections = ArrayList
() + + override val title: String = categoryTitle + override val type: RoutineType = RoutineType.Category + + fun insertSection(section: Section) { + section.category = this + + sections.add(section) + } +} + +class Section( + val sectionId: String, + val sectionTitle: String, + val description: String, + val sectionMode: SectionMode) : LinkedRoutine(), Serializable { + + var category: Category? = null + var currentLevel = 0 + set(level) { + if (sectionMode == SectionMode.Levels || sectionMode == SectionMode.Pick) { + if (level < 0 || level >= exercises.size) { + return + } + + field = level + } + } + + val exercises = ArrayList() + + override val title: String + get() = sectionTitle + + override val type: RoutineType + get() = RoutineType.Section + + fun setCurrentLevel(exercise: Exercise) { + var current = 0 + for (ex in exercises) { + if (ex == exercise) { + currentLevel = current + return + } + + current++ + } + } + + val availableLevels: Int + get() { + if (sectionMode == SectionMode.Levels || sectionMode == SectionMode.Pick) { + return exercises.size + } else { + return 0 + } + } + + fun insertExercise(exercise: Exercise) { + exercise.category = category + exercise.section = this + + exercises.add(exercise) + } + + val currentExercise: Exercise + get() = exercises[currentLevel] +} + +class Exercise( + val exerciseId: String, + val level: String, + val exerciseTitle: String, + val description: String, + val youTubeId: String, + val videoId: String, + val defaultSet: String) : LinkedRoutine(), Serializable { + + var category: Category? = null + var section: Section? = null + + var previous: Exercise? = null + var next: Exercise? = null + + override val title: String + get() = exerciseTitle + + override val type: RoutineType + get() = RoutineType.Exercise + + val isTimedSet: Boolean + get() = defaultSet.equals("timed", ignoreCase = true) + + val hasProgressions: Boolean + get() = section?.sectionMode != SectionMode.All + + val isPrevious: Boolean + get() = previous != null + + val isNext: Boolean + get() = next != null +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/model/RoutineJson.kt b/app/src/main/kotlin/com/bodyweight/fitness/model/RoutineJson.kt new file mode 100644 index 00000000..12c1626a --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/model/RoutineJson.kt @@ -0,0 +1,50 @@ +package com.bodyweight.fitness.model + +import java.util.ArrayList + +class JSONRoutine { + var routineId: String = "" + var title: String = "" + var subtitle: String = "" + var routine = ArrayList() + + val size: Int by lazy { + routine.size + } +} + +class JSONLinkedRoutine { + var categoryId: String = "" + var sectionId: String = "" + var exerciseId: String = "" + var level: String = "" + var title: String = "" + var description: String = "" + var youTubeId: String = "" + var videoId: String = "" + + private var type: String = "" + private var mode: String = "" + + var defaultSet: String = "" + + val routineType: RoutineType by lazy { + if (type.equals("category")) { + RoutineType.Category + } else if (type.equals("section")) { + RoutineType.Section + } else { + RoutineType.Exercise + } + } + + val sectionMode: SectionMode by lazy { + if (mode.equals("all")) { + SectionMode.All + } else if (mode.equals("pick")) { + SectionMode.Pick + } else { + SectionMode.Levels + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/model/RoutineRepository.kt b/app/src/main/kotlin/com/bodyweight/fitness/model/RoutineRepository.kt new file mode 100644 index 00000000..e170cf44 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/model/RoutineRepository.kt @@ -0,0 +1,295 @@ +package com.bodyweight.fitness.model + +import com.bodyweight.fitness.formatMinutes +import com.bodyweight.fitness.formatSeconds +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.utils.Preferences + +import io.realm.RealmList +import io.realm.RealmObject +import io.realm.annotations.Index +import io.realm.annotations.PrimaryKey +import io.realm.annotations.Required + +import org.joda.time.DateTime +import org.joda.time.Days +import org.joda.time.Duration +import org.joda.time.Minutes +import org.joda.time.format.PeriodFormatterBuilder + +import java.util.* + +open class RepositoryRoutine( + @PrimaryKey @Required + open var id: String = "", + + @Index + open var routineId: String = "routine0", + + open var title: String = "", + open var subtitle: String = "", + + @Index + open var startTime: Date = Date(), + + @Index + open var lastUpdatedTime: Date = Date(), + + open var categories: RealmList = RealmList(), + open var sections: RealmList = RealmList(), + open var exercises: RealmList = RealmList() +) : RealmObject() { + companion object { + fun setLastUpdatedTime(repositoryRoutine: RepositoryRoutine, isNestedTransaction: Boolean) { + val now = DateTime.now() + + val startTime = DateTime(repositoryRoutine.startTime) + val lastUpdatedTime = DateTime(repositoryRoutine.lastUpdatedTime) + + /** + * Set last updated time only if it's the same day. + * Set last updated time only if the difference in minutes is less than 180. + */ + if (Days.daysBetween(startTime.toLocalDate(), now.toLocalDate()).days == 0) { + if (Minutes.minutesBetween(startTime.toLocalDate(), lastUpdatedTime.toLocalDate()).minutes < 180) { + if (isNestedTransaction) { + repositoryRoutine.lastUpdatedTime = DateTime().toDate() + } else { + Repository.realm.executeTransaction { + repositoryRoutine.lastUpdatedTime = DateTime().toDate() + } + } + } + } + } + + fun getStartTime(repositoryRoutine: RepositoryRoutine): String { + return DateTime(repositoryRoutine.startTime) + .toString("HH:mm", Locale.ENGLISH) + } + + fun getLastUpdatedTime(repositoryRoutine: RepositoryRoutine): String { + return DateTime(repositoryRoutine.lastUpdatedTime) + .toString("HH:mm", Locale.ENGLISH) + } + + fun getWorkoutLength(repositoryRoutine: RepositoryRoutine): String { + val duration = Duration( + DateTime(repositoryRoutine.startTime), + DateTime(repositoryRoutine.lastUpdatedTime)) + + val formatter = PeriodFormatterBuilder() + .appendHours() + .appendSuffix("h ") + .appendMinutes() + .appendSuffix("m") + .toFormatter() + .print(duration.toPeriod()) + + if (formatter.replace(" ", "").equals("")) { + return "--" + } else { + return formatter + } + } + + fun getWorkoutLengthInMinutes(repositoryRoutine: RepositoryRoutine): Int { + return Duration( + DateTime(repositoryRoutine.startTime), + DateTime(repositoryRoutine.lastUpdatedTime) + ).toStandardMinutes().minutes + } + + fun getVisibleAndCompletedExercises(exercises: List): List { + return exercises.filter { + it.visible || RepositoryExercise.isCompleted(it) + } + } + + fun getNumberOfExercises(exercises: List): Int { + return getVisibleAndCompletedExercises(exercises).count() + } + + fun getNumberOfCompletedExercises(exercises: List): Int { + return exercises.filter { + RepositoryExercise.isCompleted(it) + }.count() + } + + fun getMissedExercises(exercises: List): List { + return exercises.filter { + it.visible && !RepositoryExercise.isCompleted(it) + } + } + + fun getCompletionRate(repositoryRoutine: RepositoryRoutine): CompletionRate { + val exercises = getVisibleAndCompletedExercises(repositoryRoutine.exercises) + + val numberOfExercises: Int = getNumberOfExercises(exercises) + val numberOfCompletedExercises: Int = getNumberOfCompletedExercises(exercises) + + if (numberOfExercises == 0) { + return CompletionRate(0, "0%") + } + + val completionRate = numberOfCompletedExercises * 100 / numberOfExercises + + return CompletionRate(completionRate, "$completionRate%") + } + + fun getTitleWithDate(repositoryRoutine: RepositoryRoutine): String { + val date = DateTime(repositoryRoutine.startTime).toString("EEEE, d MMMM YYYY - HH:mm") + + return "${repositoryRoutine.title} - ${repositoryRoutine.subtitle} - $date." + } + + fun toText(repositoryRoutine: RepositoryRoutine): String { + val startTime = DateTime(repositoryRoutine.startTime).toString("EEEE, d MMMM YYYY - HH:mm") + val lastUpdatedTime = RepositoryRoutine.getLastUpdatedTime(repositoryRoutine) + val workoutLength = RepositoryRoutine.getWorkoutLength(repositoryRoutine) + val weightUnit = Preferences.weightMeasurementUnit.toString() + + var content = "Hello, The following is your workout in Text/HTML format (CSV attached)." + + content += "\n\nWorkout on $startTime." + content += "\nLast Updated at $lastUpdatedTime." + content += "\nWorkout length: $workoutLength" + content += "\n\n${repositoryRoutine.title} - ${repositoryRoutine.subtitle}" + + for (exercise in RepositoryRoutine.getVisibleAndCompletedExercises(repositoryRoutine.exercises)) { + content += "\n\n${exercise.title}" + + for ((index, set) in exercise.sets.withIndex()) { + content += "\nSet ${index + 1}" + + if (set.isTimed) { + content += "\nMinutes: ${set.seconds.formatMinutes(false)}" + content += "\nSeconds: ${set.seconds.formatSeconds(false)}" + } else { + content += "\nReps: ${set.reps}" + content += "\nWeight: ${set.weight} $weightUnit" + } + } + } + + return content + } + + fun toCSV(repositoryRoutine: RepositoryRoutine): String { + val date = DateTime(repositoryRoutine.startTime).toString("d MMMM YYYY") + val startTime = getStartTime(repositoryRoutine) + val lastUpdatedTime = getLastUpdatedTime(repositoryRoutine) + val workoutLength = getWorkoutLength(repositoryRoutine) + val routineTitle = "${repositoryRoutine.title} - ${repositoryRoutine.subtitle}" + val weightUnit = Preferences.weightMeasurementUnit.toString() + + var content = "Date, Start Time, End Time, Workout Length, Routine, Exercise, Set Order, Reps, Weight, Minutes, Seconds\n" + + for (exercise in getVisibleAndCompletedExercises(repositoryRoutine.exercises)) { + for ((index, set) in exercise.sets.withIndex()) { + content += "$date,$startTime,$lastUpdatedTime,$workoutLength,$routineTitle,${exercise.title},${index + 1},${set.reps},${set.weight} $weightUnit,${set.seconds.formatMinutes(false)},${set.seconds.formatSeconds(false)}\n" + } + } + + return content + } + } +} + +open class RepositoryCategory( + @PrimaryKey @Required + open var id: String = "", + + @Index + open var categoryId: String = "", + + open var title: String = "", + + open var routine: RepositoryRoutine? = null, + + open var sections: RealmList = RealmList(), + open var exercises: RealmList = RealmList() +) : RealmObject() { + companion object { + fun getCompletionRate(repositoryCategory: RepositoryCategory): CompletionRate { + val exercises = RepositoryRoutine.getVisibleAndCompletedExercises(repositoryCategory.exercises) + + val numberOfExercises: Int = RepositoryRoutine.getNumberOfExercises(exercises) + val numberOfCompletedExercises: Int = RepositoryRoutine.getNumberOfCompletedExercises(exercises) + + if (numberOfExercises == 0) { + return CompletionRate(0, "0%") + } + + val completionRate = numberOfCompletedExercises * 100 / numberOfExercises + + return CompletionRate(completionRate, "$completionRate%") + } + } +} + +open class RepositorySection( + @PrimaryKey @Required + open var id: String = "", + + @Index + open var sectionId: String = "", + + open var title: String = "", + open var mode: String = "", + + open var routine: RepositoryRoutine? = null, + open var category: RepositoryCategory? = null, + + open var exercises: RealmList = RealmList() +) : RealmObject() {} + +open class RepositoryExercise( + @PrimaryKey @Required + open var id: String = "", + + @Index open var exerciseId: String = "", + + open var title: String = "", + open var description: String = "", + open var defaultSet: String = "timed", + + open var visible: Boolean = false, + + open var routine: RepositoryRoutine? = null, + open var category: RepositoryCategory? = null, + open var section: RepositorySection? = null, + + open var sets: RealmList = RealmList() +) : RealmObject() { + companion object { + fun isCompleted(repositoryExercise: RepositoryExercise): Boolean { + val size = repositoryExercise.sets.size + + if (size == 0) { + return false + } + + val firstSet = repositoryExercise.sets[0] + + if (size == 1 && firstSet.seconds == 0 && firstSet.reps == 0) { + return false + } + + return true + } + } +} + +open class RepositorySet( + @PrimaryKey @Required + open var id: String = "", + + open var isTimed: Boolean = false, + + open var weight: Double = 0.0, + open var reps: Int = 0, + open var seconds: Int = 0, + + open var exercise: RepositoryExercise? = null +) : RealmObject() {} diff --git a/app/src/main/kotlin/com/bodyweight/fitness/repository/Repository.kt b/app/src/main/kotlin/com/bodyweight/fitness/repository/Repository.kt new file mode 100644 index 00000000..86071919 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/repository/Repository.kt @@ -0,0 +1,165 @@ +package com.bodyweight.fitness.repository + +import org.joda.time.DateTime + +import java.util.UUID + +import com.bodyweight.fitness.App +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.stream.RoutineStream + +import io.realm.DynamicRealm +import io.realm.DynamicRealmObject +import io.realm.Realm +import io.realm.RealmConfiguration + +object Repository { + private val realmName = "bodyweight.fitness.realm" + + val realm: Realm + get() = Realm.getInstance(RealmConfiguration.Builder(App.context) + .name(realmName) + .schemaVersion(2) + .migration { realm: DynamicRealm, oldVersion: Long, newVersion: Long -> + val schema = realm.schema + val routineSchema = schema.get("RepositoryRoutine") + + if (oldVersion.toInt() == 1) { + routineSchema + .addField("title", String::class.java) + .addField("subtitle", String::class.java) + .transform { obj: DynamicRealmObject -> + obj.set("title", "Bodyweight Fitness") + obj.set("subtitle", "Recommended Routine") + } + } + } + .build()) + + fun buildRealmRoutine(routine: Routine): RepositoryRoutine { + var repositoryRoutine: RepositoryRoutine? = null + + realm.executeTransaction { + repositoryRoutine = realm.createObject(RepositoryRoutine::class.java) + repositoryRoutine?.let { + it.id = "Routine-" + UUID.randomUUID().toString() + it.routineId = routine.routineId + it.title = routine.title + it.subtitle = routine.subtitle + it.startTime = DateTime().toDate() + it.lastUpdatedTime = DateTime().toDate() + + var repositoryCategory: RepositoryCategory? = null + var repositorySection: RepositorySection? = null + + for (exercise in routine.exercises) { + val repositoryExercise = realm.createObject(RepositoryExercise::class.java) + repositoryExercise.id = "Exercise-" + UUID.randomUUID().toString() + repositoryExercise.exerciseId = exercise.exerciseId + repositoryExercise.title = exercise.title + repositoryExercise.description = exercise.description + repositoryExercise.defaultSet = exercise.defaultSet + + val repositorySet = realm.createObject(RepositorySet::class.java) + repositorySet.id = "Set-" + UUID.randomUUID().toString() + + if (exercise.defaultSet == "weighted") { + repositorySet.isTimed = false + } else { + repositorySet.isTimed = true + } + + repositorySet.seconds = 0 + repositorySet.weight = 0.0 + repositorySet.reps = 0 + repositorySet.exercise = repositoryExercise + + repositoryExercise.sets.add(repositorySet) + + if (repositoryCategory == null || !repositoryCategory.title.equals(exercise.category!!.title, ignoreCase = true)) { + repositoryCategory = realm.createObject(RepositoryCategory::class.java) + repositoryCategory!!.id = "Category-" + UUID.randomUUID().toString() + repositoryCategory.categoryId = exercise.category!!.categoryId + repositoryCategory.title = exercise.category!!.title + repositoryCategory.routine = repositoryRoutine + + it.categories.add(repositoryCategory) + } + + if (repositorySection == null || !repositorySection.title.equals(exercise.section!!.title, ignoreCase = true)) { + repositorySection = realm.createObject(RepositorySection::class.java) + repositorySection!!.id = "Section-" + UUID.randomUUID().toString() + repositorySection.sectionId = exercise.section!!.sectionId + repositorySection.title = exercise.section!!.title + repositorySection.mode = exercise.section!!.sectionMode.toString() + repositorySection.routine = repositoryRoutine + repositorySection.category = repositoryCategory + + it.sections.add(repositorySection) + repositoryCategory.sections.add(repositorySection) + } + + repositoryExercise.routine = repositoryRoutine + repositoryExercise.category = repositoryCategory + repositoryExercise.section = repositorySection + + /** + * Hide exercises not relevant to user level. + */ + if (exercise.section!!.sectionMode == SectionMode.Levels || exercise.section!!.sectionMode == SectionMode.Pick) { + if (exercise == exercise.section!!.currentExercise) { + repositoryExercise.visible = true + } else { + repositoryExercise.visible = false + } + } else { + repositoryExercise.visible = true + } + + it.exercises.add(repositoryExercise) + repositoryCategory.exercises.add(repositoryExercise) + repositorySection.exercises.add(repositoryExercise) + } + } + } + + return repositoryRoutine!! + } + + fun getRepositoryRoutineForPrimaryKeyRoutineId(primaryKeyRoutineId: String): RepositoryRoutine { + return realm.where(RepositoryRoutine::class.java).equalTo("id", primaryKeyRoutineId).findFirst() + } + + val repositoryRoutineForToday: RepositoryRoutine + get() { + val start = DateTime().withTimeAtStartOfDay().toDate() + val end = DateTime().withTimeAtStartOfDay().plusDays(1).minusSeconds(1).toDate() + + val routineId = RoutineStream.routine.routineId + + var repositoryRoutine: RepositoryRoutine? = realm.where(RepositoryRoutine::class.java) + .between("startTime", start, end) + .equalTo("routineId", routineId) + .findFirst() + + if (repositoryRoutine == null) { + repositoryRoutine = buildRealmRoutine(RoutineStream.routine) + } + + return repositoryRoutine + } + + fun repositoryRoutineForTodayExists(): Boolean { + val start = DateTime().withTimeAtStartOfDay().toDate() + val end = DateTime().withTimeAtStartOfDay().plusDays(1).minusSeconds(1).toDate() + + val routineId = RoutineStream.routine.routineId + + realm.where(RepositoryRoutine::class.java) + .between("startTime", start, end) + .equalTo("routineId", routineId) + .findFirst() ?: return false + + return true + } +} diff --git a/app/src/main/kotlin/com/bodyweight/fitness/SchemaMigration.kt b/app/src/main/kotlin/com/bodyweight/fitness/repository/SchemaMigration.kt similarity index 72% rename from app/src/main/kotlin/com/bodyweight/fitness/SchemaMigration.kt rename to app/src/main/kotlin/com/bodyweight/fitness/repository/SchemaMigration.kt index d3f067d5..ebb7acb3 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/SchemaMigration.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/repository/SchemaMigration.kt @@ -1,15 +1,14 @@ -package com.bodyweight.fitness +package com.bodyweight.fitness.repository import com.bodyweight.fitness.model.Routine -import com.bodyweight.fitness.model.repository.RepositoryRoutine -import com.bodyweight.fitness.stream.RepositoryStream +import com.bodyweight.fitness.model.RepositoryRoutine import com.bodyweight.fitness.stream.RoutineStream class SchemaMigration { fun migrateSchemaIfNeeded() { - if (RepositoryStream.getInstance().repositoryRoutineForTodayExists()) { - val routine = RoutineStream.getInstance().routine - val currentSchema = RepositoryStream.getInstance().repositoryRoutineForToday + if (Repository.repositoryRoutineForTodayExists()) { + val routine = RoutineStream.routine + val currentSchema = Repository.repositoryRoutineForToday migrateSchemaIfNeeded(routine, currentSchema) } @@ -17,9 +16,9 @@ class SchemaMigration { private fun migrateSchemaIfNeeded(routine: Routine, currentSchema: RepositoryRoutine) { if (!(isValidSchema(routine, currentSchema))) { - val newSchema = RepositoryStream.getInstance().buildRealmRoutine(routine) + val newSchema = Repository.buildRealmRoutine(routine) - val realm = RepositoryStream.getInstance().realm + val realm = Repository.realm realm.executeTransaction { newSchema.startTime = currentSchema.startTime @@ -39,7 +38,7 @@ class SchemaMigration { } } - currentSchema.removeFromRealm() + currentSchema.deleteFromRealm() realm.copyToRealmOrUpdate(newSchema) } diff --git a/app/src/main/kotlin/com/bodyweight/fitness/stream/RoutineStream.kt b/app/src/main/kotlin/com/bodyweight/fitness/stream/RoutineStream.kt new file mode 100644 index 00000000..2d898b12 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/stream/RoutineStream.kt @@ -0,0 +1,101 @@ +package com.bodyweight.fitness.stream + +import com.bodyweight.fitness.App +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.persistence.Glacier +import com.bodyweight.fitness.utils.Preferences + +import com.google.gson.Gson + +import org.apache.commons.io.IOUtils + +import java.io.IOException + +import com.bodyweight.fitness.R +import com.bodyweight.fitness.adapter.SpinnerRoutine +import com.bodyweight.fitness.extension.debug + +import rx.Observable +import rx.android.schedulers.AndroidSchedulers +import rx.subjects.PublishSubject + +class JsonRoutineLoader { + fun getRoutine(resource: Int): Routine { + try { + val raw = IOUtils.toString(App.context!!.resources.openRawResource(resource)) + val jsonRoutine = Gson().fromJson(raw, JSONRoutine::class.java) + + return Routine(jsonRoutine) + } catch (e: IOException) { + error(e.message.toString()) + } + } +} + +object RoutineStream { + private val routineSubject = PublishSubject.create() + private val exerciseSubject = PublishSubject.create() + + var routine: Routine = + if (Preferences.defaultRoutine == "routine0") { + JsonRoutineLoader().getRoutine(R.raw.bodyweight_fitness_recommended_routine) + } else { + JsonRoutineLoader().getRoutine(R.raw.molding_mobility_flexibility_routine) + } + + set(value) { + if (value.routineId.equals(routine.routineId)) { + return + } + + Preferences.defaultRoutine = value.routineId + + exercise = value.linkedExercises.first() + + routineSubject.onNext(value) + + field = value + + debug("set value of: " + routine.title) + } + + var exercise: Exercise = routine.linkedExercises.first() + set(value) { + exerciseSubject.onNext(value) + + field = value + } + + fun setRoutine(spinnerRoutine: SpinnerRoutine) { + when(spinnerRoutine.id) { + 0 -> { + routine = JsonRoutineLoader().getRoutine(R.raw.bodyweight_fitness_recommended_routine) + } + 1 -> { + routine = JsonRoutineLoader().getRoutine(R.raw.molding_mobility_flexibility_routine) + } + } + } + + fun routineObservable(): Observable { + return Observable.merge(Observable.just(routine).publish().refCount(), routineSubject) + .observeOn(AndroidSchedulers.mainThread()) + .publish() + .refCount() + } + + fun exerciseObservable(): Observable { + return Observable.merge(Observable.just(exercise).publish().refCount(), exerciseSubject) + .observeOn(AndroidSchedulers.mainThread()) + .publish() + .refCount() + } + + fun setLevel(chosenExercise: Exercise, level: Int) { + routine.setLevel(chosenExercise, level) + + exercise = chosenExercise + + Glacier.put(chosenExercise.section!!.sectionId, chosenExercise.section!!.currentExercise.exerciseId) + } +} diff --git a/app/src/main/kotlin/com/bodyweight/fitness/stream/Stream.kt b/app/src/main/kotlin/com/bodyweight/fitness/stream/Stream.kt index 8def32a9..4a5649d1 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/stream/Stream.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/stream/Stream.kt @@ -1,14 +1,13 @@ package com.bodyweight.fitness.stream -import com.bodyweight.fitness.model.CalendarDay +import com.bodyweight.fitness.R +import com.bodyweight.fitness.model.* import rx.Observable +import rx.android.schedulers.AndroidSchedulers import rx.subjects.PublishSubject -data class SetReps(val set: Int, val reps: Int) - -enum class DialogType { LogWorkout, Progress } -data class Dialog(val dialogType: DialogType, val exerciseId: String) +import kotlin.properties.Delegates object UiEvent { private val dialogSubject = PublishSubject.create() @@ -21,60 +20,102 @@ object UiEvent { } object Stream { + var currentDrawerId: Int = R.id.action_menu_home + private set + var currentCalendarPage: Int = 60 private set var currentCalendarDay: CalendarDay = CalendarDay() private set - private val mMenuSubject = PublishSubject.create() - private val mDrawerSubject = PublishSubject.create() - private val mLoggedSecondsSubject = PublishSubject.create() - private val mLoggedSetRepsSubject = PublishSubject.create() + var currentWorkoutViewType: WorkoutViewType by Delegates.notNull() + private set + + private val menuSubject = PublishSubject.create() + private val drawerSubject = PublishSubject.create() + private val loggedSecondsSubject = PublishSubject.create() + private val loggedSetRepsSubject = PublishSubject.create() - private val mCalendarPageSubject = PublishSubject.create() - private val mCalendarDaySubject = PublishSubject.create() + private val calendarPageSubject = PublishSubject.create() + private val calendarDaySubject = PublishSubject.create() + + private val currentWorkoutViewSubject = PublishSubject.create() /** * Emits when changes to repository have been made. */ - private val mRepositorySubject = PublishSubject.create() - - val menuObservable: Observable get() = mMenuSubject - val drawerObservable: Observable get() = mDrawerSubject - val loggedSecondsObservable: Observable get() = mLoggedSecondsSubject - val loggedSetRepsObservable: Observable get() = mLoggedSetRepsSubject - val calendarPageObservable: Observable get() = mCalendarPageSubject - val calendarDayObservable: Observable get() = mCalendarDaySubject - val repositoryObservable: Observable get() = mRepositorySubject - - fun setMenu(menuId: Int) { - mMenuSubject.onNext(menuId) + private val repositorySubject = PublishSubject.create() + + /** + * Observables that should be re-emitted should be functions rather than values. + */ + val menuObservable: Observable get() = menuSubject + val loggedSecondsObservable: Observable get() = loggedSecondsSubject + val loggedSetRepsObservable: Observable get() = loggedSetRepsSubject + + fun drawerObservable(): Observable { + return Observable.merge(Observable.just(currentDrawerId).publish().refCount(), drawerSubject) + .observeOn(AndroidSchedulers.mainThread()) + .publish() + .refCount() + } + + fun calendarPageObservable(): Observable { + return Observable.merge(Observable.just(currentCalendarPage).publish().refCount(), calendarPageSubject) + .observeOn(AndroidSchedulers.mainThread()) + .publish() + .refCount() + } + + fun calendarDayObservable(): Observable { + return Observable.merge(Observable.just(currentCalendarDay).publish().refCount(), calendarDaySubject) + .observeOn(AndroidSchedulers.mainThread()) + .publish() + .refCount() + } + + fun repositoryObservable(): Observable { + return repositorySubject.observeOn(AndroidSchedulers.mainThread()).publish().refCount() + } + + fun setMenu(toolbarMenuItemId: Int) { + menuSubject.onNext(toolbarMenuItemId) + } + + fun setDrawer(drawerMenuItemId: Int) { + if (!drawerMenuItemId.equals(R.id.action_menu_support_developer) + && !drawerMenuItemId.equals(R.id.action_menu_settings)) { + currentDrawerId = drawerMenuItemId + } + + drawerSubject.onNext(drawerMenuItemId) } - fun setDrawer(menuId: Int) { - mDrawerSubject.onNext(menuId) + fun setWorkoutView(workoutViewType: WorkoutViewType) { + currentWorkoutViewType = workoutViewType + currentWorkoutViewSubject.onNext(workoutViewType) } fun setLoggedSeconds(loggedSeconds: Int) { - mLoggedSecondsSubject.onNext(loggedSeconds) + loggedSecondsSubject.onNext(loggedSeconds) } fun setLoggedSetReps(setReps: SetReps) { - mLoggedSetRepsSubject.onNext(setReps) + loggedSetRepsSubject.onNext(setReps) } - fun streamPage(page: Int) { + fun setCalendarPage(page: Int) { currentCalendarPage = page - mCalendarPageSubject.onNext(page) + calendarPageSubject.onNext(page) } - fun streamDay(day: CalendarDay) { + fun setCalendarDay(day: CalendarDay) { currentCalendarDay = day - mCalendarDaySubject.onNext(day) + calendarDaySubject.onNext(day) } fun setRepository() { - mRepositorySubject.onNext(true) + repositorySubject.onNext(true) } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/ui/DashboardActivity.kt b/app/src/main/kotlin/com/bodyweight/fitness/ui/DashboardActivity.kt index 3b38c4fb..212a8380 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/ui/DashboardActivity.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/ui/DashboardActivity.kt @@ -6,7 +6,7 @@ import android.support.v7.widget.LinearLayoutManager import android.view.MenuItem import com.bodyweight.fitness.R -import com.bodyweight.fitness.adapter.DashboardAdapter +import com.bodyweight.fitness.adapter.DashboardTreeAdapter import com.bodyweight.fitness.stream.RoutineStream import com.trello.rxlifecycle.components.support.RxAppCompatActivity import com.trello.rxlifecycle.kotlin.bindToLifecycle @@ -14,11 +14,11 @@ import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.activity_dashboard.* class DashboardActivity : RxAppCompatActivity() { - val dashboardAdapter: DashboardAdapter by lazy { - val routine = RoutineStream.getInstance().routine - val exercise = RoutineStream.getInstance().exercise + val dashboardTreeAdapter: DashboardTreeAdapter by lazy { + val routine = RoutineStream.routine + val exercise = RoutineStream.exercise - DashboardAdapter(routine, exercise) + DashboardTreeAdapter(routine, exercise) } override fun onCreate(savedInstanceState: Bundle?) { @@ -37,20 +37,20 @@ class DashboardActivity : RxAppCompatActivity() { it.setDisplayHomeAsUpEnabled(true) } - dashboardAdapter.asObservable().bindToLifecycle(this).subscribe { - RoutineStream.getInstance().exercise = it + dashboardTreeAdapter.asObservable().bindToLifecycle(this).subscribe { + RoutineStream.exercise = it supportFinishAfterTransition() } view_dashboard_list.layoutManager = LinearLayoutManager(this) - view_dashboard_list.adapter = dashboardAdapter + view_dashboard_list.adapter = dashboardTreeAdapter } override fun onResume() { super.onResume() - view_dashboard_list.scrollToPosition(dashboardAdapter.scrollPosition) + view_dashboard_list.scrollToPosition(dashboardTreeAdapter.scrollPosition) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/kotlin/com/bodyweight/fitness/ui/IntroductionActivity.kt b/app/src/main/kotlin/com/bodyweight/fitness/ui/IntroductionActivity.kt new file mode 100644 index 00000000..0be3d803 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/ui/IntroductionActivity.kt @@ -0,0 +1,72 @@ +package com.bodyweight.fitness.ui + +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import com.github.paolorotolo.appintro.AppIntro +import com.bodyweight.fitness.R + +class Slide : Fragment() { + private var layoutResId: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (arguments != null && arguments.containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = arguments.getInt(ARG_LAYOUT_RES_ID) + } + } + + override fun onCreateView( + inflater: LayoutInflater?, + container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + return inflater!!.inflate(layoutResId, container, false) + } + + companion object { + private val ARG_LAYOUT_RES_ID = "layoutResId" + + fun with(layoutResId: Int): Slide { + val sampleSlide = Slide() + + val args = Bundle() + args.putInt(ARG_LAYOUT_RES_ID, layoutResId) + sampleSlide.arguments = args + + return sampleSlide + } + } +} + +class IntroductionActivity : AppIntro() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + addSlide(Slide.with(R.layout.introduction_slide1)) + addSlide(Slide.with(R.layout.introduction_slide2)) + addSlide(Slide.with(R.layout.introduction_slide3)) + addSlide(Slide.with(R.layout.introduction_slide4)) + addSlide(Slide.with(R.layout.introduction_slide5)) + addSlide(Slide.with(R.layout.introduction_slide6)) + + showSkipButton(false) + } + + override fun onDonePressed(currentFragment: Fragment?) { + super.onDonePressed(currentFragment) + + loadMainActivity() + } + + fun loadMainActivity() { + startActivity(Intent(this, MainActivity::class.java)) + + finish() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/ui/MainActivity.kt b/app/src/main/kotlin/com/bodyweight/fitness/ui/MainActivity.kt index 1f249da4..9f8929f0 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/ui/MainActivity.kt @@ -4,70 +4,49 @@ import android.content.Intent import android.content.SharedPreferences import android.net.Uri import android.os.Bundle -import android.support.design.widget.TabLayout import android.support.v4.view.GravityCompat import android.support.v7.app.ActionBarDrawerToggle - import android.view.Menu + import android.view.MenuItem -import android.view.View import android.view.WindowManager import com.bodyweight.fitness.Constants -import com.bodyweight.fitness.stream.DialogType -import com.bodyweight.fitness.stream.RepositoryStream +import com.bodyweight.fitness.repository.Repository import com.bodyweight.fitness.R -import com.bodyweight.fitness.extension.debug -import com.bodyweight.fitness.stream.RoutineStream +import com.bodyweight.fitness.dialog.LogWorkoutDialog +import com.bodyweight.fitness.dialog.ProgressDialog +import com.bodyweight.fitness.model.DialogType import com.bodyweight.fitness.stream.Stream import com.bodyweight.fitness.stream.UiEvent -import com.bodyweight.fitness.utils.ApplicationStoreUtils -import com.bodyweight.fitness.utils.PreferenceUtils -import com.bodyweight.fitness.view.dialog.LogWorkoutDialog -import com.bodyweight.fitness.view.dialog.ProgressDialog +import com.bodyweight.fitness.utils.Preferences import com.kobakei.ratethisapp.RateThisApp +import com.trello.rxlifecycle.ActivityEvent import com.trello.rxlifecycle.components.support.RxAppCompatActivity -import com.trello.rxlifecycle.kotlin.bindToLifecycle +import com.trello.rxlifecycle.kotlin.bindUntilEvent import kotlinx.android.synthetic.main.activity_main.* -import kotlinx.android.synthetic.main.view_home.* -import kotlinx.android.synthetic.main.view_timer.* import kotlinx.android.synthetic.main.view_toolbar.* class MainActivity : RxAppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener { - private var mId: Int = R.id.action_menu_home - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (savedInstanceState != null) { - mId = savedInstanceState.getInt("ID") - } - setContentView(R.layout.activity_main) - setToolbar() - setTabLayout() - keepScreenOnWhenAppIsRunning() - } - - override fun onResume() { - super.onResume() +// requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + setToolbar() keepScreenOnWhenAppIsRunning() - } - override fun onStart() { - super.onStart() + val event = ActivityEvent.DESTROY UiEvent.dialogObservable - .bindToLifecycle(this) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } + .bindUntilEvent(this, event) .subscribe { - if (it.dialogType === DialogType.LogWorkout) { + if (it.dialogType === DialogType.MainActivityLogWorkout) { val bundle = Bundle() bundle.putString(Constants.exerciseId, it.exerciseId) @@ -84,72 +63,51 @@ class MainActivity : RxAppCompatActivity(), SharedPreferences.OnSharedPreference } } - RoutineStream.getInstance() - .exerciseObservable - .bindToLifecycle(this) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .subscribe { - if (it.isTimedSet) { - if (view_tabs.tabCount == 2) { - view_tabs.removeAllTabs() - view_tabs.addTab(view_tabs.newTab().setText("Timer")) - } - - view_tabs.getTabAt(0)?.select() - } else { - if (view_tabs.tabCount == 1) { - view_tabs.removeAllTabs() - - view_tabs.addTab(view_tabs.newTab().setText("Timer")) - view_tabs.addTab(view_tabs.newTab().setText("Reps Logger")) - } - - view_tabs.getTabAt(1)?.select() - } - } - Stream.menuObservable - .bindToLifecycle(this) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } + .bindUntilEvent(this, event) .filter { it == R.id.action_dashboard } .subscribe { startActivity(Intent(this, DashboardActivity::class.java)) } - - Stream.drawerObservable - .bindToLifecycle(this) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .filter { it == R.id.action_menu_home || it == R.id.action_menu_workout_log } + Stream.drawerObservable() + .bindUntilEvent(this, event) .subscribe { - mId = it - } + invalidateOptionsMenu(); - Stream.drawerObservable - .bindToLifecycle(this) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .subscribe { when (it) { R.id.action_menu_support_developer -> { - val installerPackageName = packageManager.getInstallerPackageName(packageName) - val intent = Intent(Intent.ACTION_VIEW) - - intent.data = Uri.parse(ApplicationStoreUtils.getApplicationStoreUrl(installerPackageName).applicationStoreAppUrl) - - startActivity(intent) + startActivity(Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(Constants.googlePlayUrl) + }) } R.id.action_menu_settings -> { - startActivity( - Intent(applicationContext, SettingsActivity::class.java)) + startActivity(Intent(applicationContext, SettingsActivity::class.java)) + } + + else -> { + navigation_view.setCheckedItem(it) } } } + if (!Preferences.introductionShown) { + Preferences.introductionShown = true + + startActivity(Intent(this, IntroductionActivity::class.java)) + } + } + + override fun onResume() { + super.onResume() + + keepScreenOnWhenAppIsRunning() + } + + override fun onStart() { + super.onStart() + RateThisApp.onStart(this) RateThisApp.showRateDialogIfNeeded(this) } @@ -159,13 +117,7 @@ class MainActivity : RxAppCompatActivity(), SharedPreferences.OnSharedPreference clearFlagKeepScreenOn() - RepositoryStream.getInstance().realm.close() - } - - public override fun onSaveInstanceState(outState: Bundle) { - outState.putInt("ID", mId) - - super.onSaveInstanceState(outState) + Repository.realm.close() } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -174,18 +126,19 @@ class MainActivity : RxAppCompatActivity(), SharedPreferences.OnSharedPreference return super.onOptionsItemSelected(item) } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { - keepScreenOnWhenAppIsRunning() - } + override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + menu?.clear() - override fun onPrepareOptionsMenu(menu: Menu): Boolean { - if (mId == R.id.action_menu_home) { - menuInflater.inflate(R.menu.home, menu) - } else if (mId == R.id.action_menu_workout_log) { - menuInflater.inflate(R.menu.calendar, menu) + when (Stream.currentDrawerId) { + R.id.action_menu_workout -> menuInflater.inflate(R.menu.menu_workout, menu) + R.id.action_menu_workout_log -> menuInflater.inflate(R.menu.menu_log_workout, menu) } - return super.onCreateOptionsMenu(menu) + return super.onPrepareOptionsMenu(menu) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + keepScreenOnWhenAppIsRunning() } private fun setToolbar() { @@ -222,30 +175,8 @@ class MainActivity : RxAppCompatActivity(), SharedPreferences.OnSharedPreference } } - private fun setTabLayout() { - view_tabs.addTab(view_tabs.newTab().setText("Timer")) - view_tabs.addTab(view_tabs.newTab().setText("Reps Logger")) - - view_tabs.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - val position = tab.position - - if (position == 0) { - timer_view.visibility = View.VISIBLE - reps_logger_view.visibility = View.GONE - } else { - timer_view.visibility = View.GONE - reps_logger_view.visibility = View.VISIBLE - } - } - - override fun onTabUnselected(tab: TabLayout.Tab) { } - override fun onTabReselected(tab: TabLayout.Tab) { } - }) - } - private fun keepScreenOnWhenAppIsRunning() { - if (PreferenceUtils.getInstance().keepScreenOnWhenAppIsRunning()) { + if (Preferences.keepScreenOnWhenAppIsRunning()) { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } else { clearFlagKeepScreenOn() diff --git a/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressActivity.kt b/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressActivity.kt index b034bb09..3c7c3d8f 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressActivity.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressActivity.kt @@ -7,17 +7,18 @@ import android.view.MenuItem import com.bodyweight.fitness.Constants import com.bodyweight.fitness.adapter.ProgressPagerAdapter -import com.bodyweight.fitness.stream.DialogType -import com.bodyweight.fitness.stream.RepositoryStream +import com.bodyweight.fitness.repository.Repository import org.joda.time.DateTime import java.util.Locale import com.bodyweight.fitness.R -import com.bodyweight.fitness.model.repository.RepositoryRoutine +import com.bodyweight.fitness.dialog.LogWorkoutDialog +import com.bodyweight.fitness.model.DialogType +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.stream.Stream import com.bodyweight.fitness.stream.UiEvent -import com.bodyweight.fitness.view.dialog.LogWorkoutDialog import com.trello.rxlifecycle.components.support.RxAppCompatActivity import com.trello.rxlifecycle.kotlin.bindToLifecycle @@ -26,7 +27,17 @@ import kotlinx.android.synthetic.main.activity_progress.* class ProgressActivity : RxAppCompatActivity() { val primaryKeyRoutineId: String by lazy { - intent.getStringExtra(Constants.PRIMARY_KEY_ROUTINE_ID) + intent.getStringExtra(Constants.primaryKeyRoutineId) + } + + val repositoryRoutine: RepositoryRoutine by lazy { + Repository.realm.where(RepositoryRoutine::class.java) + .equalTo("id", primaryKeyRoutineId) + .findFirst() + } + + val progressPagerAdapter: ProgressPagerAdapter by lazy { + ProgressPagerAdapter(repositoryRoutine) } override fun onCreate(savedInstanceState: Bundle?) { @@ -34,13 +45,6 @@ class ProgressActivity : RxAppCompatActivity() { setContentView(R.layout.activity_progress) - val realm = RepositoryStream.getInstance().realm - val repositoryRoutine = realm.where(RepositoryRoutine::class.java) - .equalTo("id", primaryKeyRoutineId) - .findFirst() - - val progressPagerAdapter = ProgressPagerAdapter(repositoryRoutine) - view_progress_pager.offscreenPageLimit = 4 view_progress_pager.adapter = progressPagerAdapter @@ -77,7 +81,7 @@ class ProgressActivity : RxAppCompatActivity() { UiEvent.dialogObservable .bindToLifecycle(this) - .filter { it.dialogType == DialogType.LogWorkout } + .filter { it.dialogType == DialogType.ProgressActivityLogWorkout } .subscribe { dialog -> val bundle = Bundle() bundle.putString(Constants.primaryKeyRoutineId, primaryKeyRoutineId) @@ -88,6 +92,12 @@ class ProgressActivity : RxAppCompatActivity() { logWorkoutDialog.show(supportFragmentManager, "dialog") } + + Stream.repositoryObservable() + .bindToLifecycle(this) + .subscribe { + progressPagerAdapter.onRepositoryUpdated() + } } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressExerciseActivity.kt b/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressExerciseActivity.kt new file mode 100644 index 00000000..eb33169f --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/ui/ProgressExerciseActivity.kt @@ -0,0 +1,261 @@ +package com.bodyweight.fitness.ui + +import android.graphics.Color +import android.os.Bundle +import android.support.v7.app.ActionBar +import com.bodyweight.fitness.* + +import com.bodyweight.fitness.adapter.RepsAdapter +import com.bodyweight.fitness.adapter.TimeAdapter +import com.bodyweight.fitness.extension.debug +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.repository.Repository + +import com.trello.rxlifecycle.components.support.RxAppCompatActivity +import com.trello.rxlifecycle.kotlin.bindToLifecycle + +import kotlinx.android.synthetic.main.activity_progress_card_set.view.* +import kotlinx.android.synthetic.main.activity_progress_exercise.* + +import org.joda.time.DateTime +import rx.android.schedulers.AndroidSchedulers +import java.util.* + +class ProgressExerciseActivity : RxAppCompatActivity() { + val exerciseId: String by lazy { + intent.getStringExtra(Constants.exerciseId) + } + + val repositoryExercise: RepositoryExercise? by lazy { + Repository.realm.where(RepositoryExercise::class.java) + .equalTo("exerciseId", exerciseId) + .findFirst() + } + + val repsAdapter by lazy { + RepsAdapter() + } + + val timeAdapter by lazy { + TimeAdapter() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_progress_exercise) + + setSupportActionBar(toolbar) + + supportActionBar?.let { + val actionBar = it + + it.title = "Exercise History" + + repositoryExercise?.let { + if (it.defaultSet == "timed") { + actionBar.subtitle = "Time Graph" + } else { + actionBar.subtitle = "Reps Graph" + } + } + + it.elevation = 0f + it.displayOptions = ActionBar.DISPLAY_SHOW_HOME or ActionBar.DISPLAY_HOME_AS_UP or ActionBar.DISPLAY_SHOW_TITLE + it.setHomeButtonEnabled(true) + it.setDisplayHomeAsUpEnabled(true) + } + + graph_view.baseLineColor = Color.WHITE + graph_view.scrubLineColor = Color.parseColor("#111111") + graph_view.isScrubEnabled = true + graph_view.animateChanges = false + graph_view.setScrubListener { + val data = it as? DateTimeRepositorySet + + data?.let { + updateTitle(it) + } + } + + repositoryExercise?.let { + if (it.defaultSet == "timed") { + graph_view.adapter = timeAdapter + + updateGraph(timeAdapter) + } else { + graph_view.adapter = repsAdapter + + updateGraph(repsAdapter) + } + } + + updateList() + } + + fun updateTitle(data: DateTimeRepositorySet) { + graph_title.text = data.dateTime.toString("dd MMMM, YYYY", Locale.ENGLISH) + + if (data.repositorySet != null) { + if (data.repositorySet.isTimed) { + val rawSeconds = data.repositorySet.seconds + + val stringMinutes = rawSeconds.formatMinutes(format = false) + val numberOfMinutes = rawSeconds.formatMinutesAsNumber() + val stringSeconds = rawSeconds.formatSeconds(format = false) + val numberOfSeconds = rawSeconds.formatSecondsAsNumber() + + val minutes = if (numberOfMinutes == 1) { "Minute" } else { "Minutes" } + val seconds = if (numberOfSeconds == 1) { "Second" } else { "Seconds" } + + if (rawSeconds < 60) { + graph_description.text = "$stringSeconds $seconds" + } else if (numberOfSeconds == 0 || numberOfSeconds == 60) { + graph_description.text = "$stringMinutes $minutes" + } else { + graph_description.text = "$stringMinutes $minutes $stringSeconds $seconds" + } + } else { + val reps = if (data.repositorySet.reps == 1) { "Rep" } else { "Reps" } + + graph_description.text = "${data.repositorySet.reps} $reps" + } + } else { + graph_description.text = "Not Completed" + } + } + + fun updateGraph(adapter: RepsAdapter) { + Repository.realm.where(RepositoryExercise::class.java) + .equalTo("exerciseId", exerciseId) + .findAllAsync() + .asObservable() + .filter { it.isLoaded } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(this) + .subscribe { + if (it.isNotEmpty()) { + val list = ArrayList() + + for (repositoryExercise in it) { + val date = DateTime(repositoryExercise.routine?.startTime) + + for (repositorySet in repositoryExercise.sets.filter { !it.isTimed }) { + list.add(DateTimeRepositorySet(date, repositorySet)) + } + } + + adapter.changeData(list) + + if (list.size > 1) { + updateTitle(list.first()) + } else { + graph_card_view.setGone() + } + } else { + graph_card_view.setGone() + } + } + } + + fun updateGraph(adapter: TimeAdapter) { + Repository.realm.where(RepositoryExercise::class.java) + .equalTo("exerciseId", exerciseId) + .findAllAsync() + .asObservable() + .filter { it.isLoaded } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(this) + .subscribe { + if (it.isNotEmpty()) { + val list = ArrayList() + + for (repositoryExercise in it) { + val date = DateTime(repositoryExercise.routine?.startTime) + + for (repositorySet in repositoryExercise.sets.filter { it.isTimed }) { + list.add(DateTimeRepositorySet(date, repositorySet)) + } + } + + adapter.changeData(list) + + if (list.size > 1) { + updateTitle(list.first()) + } else { + graph_card_view.setGone() + } + } else { + graph_card_view.setGone() + } + } + } + + fun updateList() { + Repository.realm.where(RepositoryExercise::class.java) + .equalTo("exerciseId", exerciseId) + .findAllAsync() + .asObservable() + .filter { it.isLoaded } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(this) + .subscribe { + if (it.isNotEmpty()) { + it.firstOrNull()?.let { + exercise_title.text = it.title + exercise_description.text = "Data" + } + + exercise_sets.removeAllViews() + + for (repositoryExercise in it) { + val date = DateTime(repositoryExercise.routine?.startTime).toString("d MMMM YYYY") + + for ((index, repositorySet) in repositoryExercise.sets.withIndex()) { + val view = exercise_sets.inflate(R.layout.activity_progress_card_set) + + if (repositorySet.isTimed) { + val rawSeconds = repositorySet.seconds + + val stringMinutes = rawSeconds.formatMinutes(format = false) + val numberOfMinutes = rawSeconds.formatMinutesAsNumber() + val stringSeconds = rawSeconds.formatSeconds(format = false) + val numberOfSeconds = rawSeconds.formatSecondsAsNumber() + + val minutes = if (numberOfMinutes == 1) { "Minute" } else { "Minutes" } + val seconds = if (numberOfSeconds == 1) { "Second" } else { "Seconds" } + + if (rawSeconds < 60) { + view.left_value.text = "$stringSeconds $seconds" + } else if (numberOfSeconds == 0 || numberOfSeconds == 60) { + view.left_value.text = "$stringMinutes $minutes" + } else { + view.left_value.text = "$stringMinutes $minutes, $stringSeconds $seconds" + } + + view.left_label.text = "$date - Set ${index + 1}" + + view.right_value.text = "" + view.right_label.text = "" + } else { + val reps = if (repositorySet.reps == 1) { "Rep" } else { "Reps" } + + view.left_value.text = "${repositorySet.reps} $reps" + view.left_label.text = "$date - Set ${index + 1}" + + if (repositorySet.weight > 0.0) { + view.right_value.text = "${repositorySet.weight}" + view.right_label.text = "Weight" + } else { + view.right_value.text = "" + view.right_label.text = "" + } + } + + exercise_sets.addView(view) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/ui/SettingsActivity.kt b/app/src/main/kotlin/com/bodyweight/fitness/ui/SettingsActivity.kt index c15767e1..39fa4cac 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/ui/SettingsActivity.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/ui/SettingsActivity.kt @@ -47,7 +47,7 @@ class SettingsFragment : PreferenceFragment(), SharedPreferences.OnSharedPrefere addPreferencesFromResource(R.xml.settings) - updatePreferenceSummaryForKey(Constants.PREFERENCE_WEIGHT_MEASUREMENT_UNITS) + updatePreferenceSummaryForKey(Constants.preferencesWeightMeasurementUnitsKey) } override fun onResume() { @@ -67,7 +67,7 @@ class SettingsFragment : PreferenceFragment(), SharedPreferences.OnSharedPrefere } fun updatePreferenceSummaryForKey(key: String) { - if (key.matches(Constants.PREFERENCE_WEIGHT_MEASUREMENT_UNITS.toRegex())) { + if (key.matches(Constants.preferencesWeightMeasurementUnitsKey.toRegex())) { val listPreference = findPreference(key) as ListPreference listPreference.summary = listPreference.entry diff --git a/app/src/main/kotlin/com/bodyweight/fitness/utils/Preferences.kt b/app/src/main/kotlin/com/bodyweight/fitness/utils/Preferences.kt new file mode 100644 index 00000000..cd344bd1 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/utils/Preferences.kt @@ -0,0 +1,85 @@ +package com.bodyweight.fitness.utils + +import android.content.SharedPreferences +import android.preference.PreferenceManager + +import com.bodyweight.fitness.App +import com.bodyweight.fitness.Constants + +import com.bodyweight.fitness.R +import com.bodyweight.fitness.model.WeightMeasurementUnit + +object Preferences { + init { + PreferenceManager.setDefaultValues(App.context, R.xml.settings, false) + } + + var introductionShown: Boolean + get() { + return getSharedPreferences() + .getBoolean(Constants.preferencesIntroductionShown, false) + } + + set(value) { + getSharedPreferences() + .edit() + .putBoolean(Constants.preferencesIntroductionShown, value) + .commit() + } + + var defaultRoutine: String + get() { + return getSharedPreferences() + .getString(Constants.preferencesDefaultRoutineKey, "routine0") + } + + set(value) { + getSharedPreferences() + .edit() + .putString(Constants.preferencesDefaultRoutineKey, value) + .commit() + } + + val weightMeasurementUnit: WeightMeasurementUnit + get() { + val value = getSharedPreferences().getString(Constants.preferencesWeightMeasurementUnitsKey, "kg") + + if (value.equals("kg", ignoreCase = true)) { + return WeightMeasurementUnit.Kg + } + + return WeightMeasurementUnit.Lbs + } + + fun playSoundWhenTimerStops(): Boolean { + return getSharedPreferences().getBoolean(Constants.preferencesPlaySoundWhenTimerStopsKey, true) + } + + fun automaticallyLogWorkoutTime(): Boolean { + return getSharedPreferences().getBoolean(Constants.preferencesAutomaticallyLogWorkoutTimeKey, true) + } + + fun keepScreenOnWhenAppIsRunning(): Boolean { + return getSharedPreferences().getBoolean(Constants.preferencesKeepScreenOnKey, true) + } + + fun setTimerValue(exerciseId: String, value: Long) { + getSharedPreferences().edit().putLong(String.format("%s%s", Constants.preferencesTimerKey, exerciseId), value).commit() + } + + fun setNumberOfReps(exerciseId: String, value: Int) { + getSharedPreferences().edit().putInt(String.format("%s%s", Constants.preferencesNumberOfRepsKey, exerciseId), value).commit() + } + + fun getTimerValueForExercise(exerciseId: String, defaultValue: Long): Long { + return getSharedPreferences().getLong(String.format("%s%s", Constants.preferencesTimerKey, exerciseId), defaultValue) + } + + fun getNumberOfRepsForExercise(exerciseId: String, defaultValue: Int): Int { + return getSharedPreferences().getInt(String.format("%s%s", Constants.preferencesNumberOfRepsKey, exerciseId), defaultValue) + } + + private fun getSharedPreferences(): SharedPreferences { + return PreferenceManager.getDefaultSharedPreferences(App.context) + } +} diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter+Extensions.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter+Extensions.kt deleted file mode 100644 index 78a181ea..00000000 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter+Extensions.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.bodyweight.fitness.view - -import com.bodyweight.fitness.model.Exercise -import com.bodyweight.fitness.stream.RoutineStream - -import rx.Observable - -fun AbstractPresenter.getCurrentExercise(): Exercise = - RoutineStream.getInstance().exercise - -fun AbstractPresenter.getExerciseObservable(): Observable = - RoutineStream.getInstance().exerciseObservable \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter.kt index 9f3c91c0..4836b66b 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/AbstractViewPresenter.kt @@ -34,10 +34,10 @@ abstract class AbstractPresenter : Serializable { } abstract class AbstractView : RelativeLayout { - abstract var mPresenter: AbstractPresenter + abstract var presenter: AbstractPresenter - val superStateKey = "superState" - val presenterKey = "presenter" + val superStateKey = this.javaClass.canonicalName + "superState" + val presenterKey = this.javaClass.canonicalName + "presenter" constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -48,33 +48,33 @@ abstract class AbstractView : RelativeLayout { this.onCreateView() - mPresenter.bindView(this) + presenter.bindView(this) } open fun onCreateView() {} open fun updateView() { - mPresenter.updateView() + presenter.updateView() } override fun onSaveInstanceState(): Parcelable? { - mPresenter.saveView() + presenter.saveView() val state = Bundle() state.putParcelable(superStateKey, super.onSaveInstanceState()); - state.putSerializable(presenterKey, mPresenter); + state.putSerializable(presenterKey, presenter); return state; } override fun onRestoreInstanceState(state: Parcelable?) { if (state is Bundle) { - mPresenter = state.getSerializable(presenterKey) as AbstractPresenter + presenter = state.getSerializable(presenterKey) as AbstractPresenter super.onRestoreInstanceState(state.getParcelable(superStateKey)) } - mPresenter.restoreView(this) + presenter.restoreView(this) } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/ActionViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/ActionViewPresenter.kt index d867f801..44764307 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/ActionViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/ActionViewPresenter.kt @@ -11,9 +11,10 @@ import android.view.View import com.bodyweight.fitness.Constants import com.bodyweight.fitness.R -import com.bodyweight.fitness.extension.debug import com.bodyweight.fitness.formatMinutes import com.bodyweight.fitness.formatSeconds +import com.bodyweight.fitness.model.DialogType +import com.bodyweight.fitness.repository.Repository import com.bodyweight.fitness.stream.* import com.bodyweight.fitness.ui.ProgressActivity import com.bodyweight.fitness.view.widget.ActionButton @@ -24,22 +25,16 @@ import com.gordonwong.materialsheetfab.MaterialSheetFabEventListener import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.view_action.view.* -object ActionShared { - var id: Int = R.id.action_menu_home -} - class ActionPresenter : AbstractPresenter() { override fun bindView(view: AbstractView) { super.bindView(view) val view = (mView as ActionView) - getExerciseObservable() + RoutineStream.exerciseObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { - if (it.hasProgressions()) { + if (it.hasProgressions) { view.setActionButtonImageDrawable(R.drawable.action_progression_white) view.showActionSheetChooseProgression() } else { @@ -48,17 +43,15 @@ class ActionPresenter : AbstractPresenter() { } } - Stream.drawerObservable + Stream.drawerObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .filter { - it.equals(R.id.action_menu_home) || it.equals(R.id.action_menu_workout_log) + it.equals(R.id.action_menu_home) + || it.equals(R.id.action_menu_workout) + || it.equals(R.id.action_menu_workout_log) } .subscribe { - ActionShared.id = it - - if (it == R.id.action_menu_home) { + if (it == R.id.action_menu_workout) { view.showActionButtons() } else { view.hideActionButtons() @@ -67,8 +60,6 @@ class ActionPresenter : AbstractPresenter() { Stream.loggedSecondsObservable .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { val format = String.format("Logged time %s:%s", it.formatMinutes(), it.formatSeconds()) @@ -77,8 +68,6 @@ class ActionPresenter : AbstractPresenter() { Stream.loggedSetRepsObservable .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { val format = String.format("Logged Set %d with %d Reps", it.set, it.reps) @@ -91,7 +80,7 @@ class ActionPresenter : AbstractPresenter() { val view = (mView as ActionView) - if (ActionShared.id == R.id.action_menu_home) { + if (Stream.currentDrawerId == R.id.action_menu_workout) { view.showActionButtons() } else { view.hideActionButtons() @@ -99,12 +88,12 @@ class ActionPresenter : AbstractPresenter() { } fun logWorkout() { - UiEvent.showDialog(DialogType.LogWorkout, getCurrentExercise().exerciseId) + UiEvent.showDialog(DialogType.MainActivityLogWorkout, RoutineStream.exercise.exerciseId) } fun watchFullVideo() { val view = (mView as ActionView) - val id = RoutineStream.getInstance().exercise.youTubeId + val id = RoutineStream.exercise.youTubeId try { view.context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:" + id))) @@ -114,29 +103,29 @@ class ActionPresenter : AbstractPresenter() { } fun chooseProgression() { - UiEvent.showDialog(DialogType.Progress, getCurrentExercise().exerciseId) + UiEvent.showDialog(DialogType.Progress, RoutineStream.exercise.exerciseId) } fun todaysWorkout() { val view = (mView as ActionView) - val routineId = RepositoryStream.getInstance().repositoryRoutineForToday.id + val routineId = Repository.repositoryRoutineForToday.id view.context.startActivity(Intent(view.context, ProgressActivity::class.java) - .putExtra(Constants.PRIMARY_KEY_ROUTINE_ID, routineId)) + .putExtra(Constants.primaryKeyRoutineId, routineId)) } } open class ActionView : AbstractView { - override var mPresenter: AbstractPresenter = ActionPresenter() + override var presenter: AbstractPresenter = ActionPresenter() - internal var mMaterialSheet: MaterialSheetFab? = null + var mMaterialSheet: MaterialSheetFab? = null constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) override fun onCreateView() { - val presenter = (mPresenter as ActionPresenter) + val presenter = (presenter as ActionPresenter) mMaterialSheet = MaterialSheetFab( action_view_action_button, diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarPageViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarPageViewPresenter.kt index 57c8f4fe..52f102bf 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarPageViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarPageViewPresenter.kt @@ -6,7 +6,6 @@ import android.util.AttributeSet import android.widget.TextView import com.bodyweight.fitness.R -import com.bodyweight.fitness.extension.debug import com.bodyweight.fitness.isRoutineLogged import com.bodyweight.fitness.isToday import com.bodyweight.fitness.model.CalendarDay @@ -17,26 +16,26 @@ import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.view_calendar_page.view.* class CalendarPagePresenter : AbstractPresenter() { - var mViewPagerPosition = 0 - var mIsTodaysWeek = false - var mIsTodaysDate = 3 + var viewPagerPosition = 0 + var isTodaysWeek = false + var isTodaysDate = 3 override fun updateView() { super.updateView() val view = (mView as CalendarPageView) - val firstDayOfTheWeek = CalendarDay(mViewPagerPosition, 0).getDate() + val firstDayOfTheWeek = CalendarDay(viewPagerPosition, 0).getDate() for (index in 0..6) { val currentDayOfTheWeek = firstDayOfTheWeek.plusDays(index) if (currentDayOfTheWeek.isToday()) { - mIsTodaysWeek = true - mIsTodaysDate = index + isTodaysWeek = true + isTodaysDate = index view.setActive(index) - if (Stream.currentCalendarPage == mViewPagerPosition) { + if (Stream.currentCalendarPage == viewPagerPosition) { view.select(index) clickedAt(index) } @@ -48,28 +47,24 @@ class CalendarPagePresenter : AbstractPresenter() { view.setText(index, currentDayOfTheWeek.dayOfMonth().asText) } - Stream.calendarPageObservable + Stream.calendarPageObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .filter { it == mViewPagerPosition } + .filter { it == viewPagerPosition } .subscribe { - if (mIsTodaysWeek) { - view.select(mIsTodaysDate) - clickedAt(mIsTodaysDate) + if (isTodaysWeek) { + view.select(isTodaysDate) + clickedAt(isTodaysDate) } else { view.select(3) clickedAt(3) } } - Stream.calendarDayObservable + Stream.calendarDayObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { - if (it.page != mViewPagerPosition) { - view.unselect(mIsTodaysDate) + if (it.page != viewPagerPosition) { + view.unselect(isTodaysDate) } else { view.select(it.day) } @@ -77,16 +72,16 @@ class CalendarPagePresenter : AbstractPresenter() { } fun clickedAt(dayView: Int) { - Stream.streamDay(CalendarDay(mViewPagerPosition, dayView)) + Stream.setCalendarDay(CalendarDay(viewPagerPosition, dayView)) } } open class CalendarPageView : AbstractView { - override var mPresenter: AbstractPresenter = CalendarPagePresenter() + override var presenter: AbstractPresenter = CalendarPagePresenter() - var mClickedDay: Int = 3 + var clickedDay: Int = 3 - val mDayViews: List by lazy { + val dayViews: List by lazy { listOf(this.day_1, this.day_2, this.day_3, this.day_4, this.day_5, this.day_6, this.day_7) } @@ -101,33 +96,33 @@ open class CalendarPageView : AbstractView { } fun setListener(dayView: Int) { - val view: TextView? = mDayViews[dayView] + val view: TextView? = dayViews[dayView] view?.setOnClickListener { select(dayView) - (mPresenter as CalendarPagePresenter).clickedAt(dayView) + (presenter as CalendarPagePresenter).clickedAt(dayView) } } fun select(dayView: Int) { - val view: TextView? = mDayViews[dayView] + val view: TextView? = dayViews[dayView] - unselect(mClickedDay) + unselect(clickedDay) view?.let { it.setTextColor(Color.parseColor("#ffffff")) it.setBackgroundResourceWithPadding(R.drawable.rounded_corner_today) } - mClickedDay = dayView + clickedDay = dayView } fun unselect(dayView: Int) { - val view: TextView? = mDayViews[dayView] + val view: TextView? = dayViews[dayView] val isToday = view?.tag as? Boolean ?: false - val clickedView: TextView? = mDayViews[mClickedDay] + val clickedView: TextView? = dayViews[clickedDay] clickedView?.let { if (isToday) { @@ -141,19 +136,19 @@ open class CalendarPageView : AbstractView { } fun setActive(dayView: Int) { - val view: TextView = mDayViews[dayView] + val view: TextView = dayViews[dayView] view.setBackgroundResourceWithPadding(R.drawable.rounded_corner_active) } fun setIsToday(dayView: Int, tag: Boolean) { - val view: TextView = mDayViews[dayView] + val view: TextView = dayViews[dayView] view.tag = tag } fun showDot(dayView: Int, show: Boolean) { - val view: TextView = mDayViews[dayView] + val view: TextView = dayViews[dayView] if (show) { view.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, R.drawable.dot) @@ -163,7 +158,7 @@ open class CalendarPageView : AbstractView { } fun setText(dayView: Int, text: String) { - val view: TextView = mDayViews[dayView] + val view: TextView = dayViews[dayView] view.text = text } diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarViewPresenter.kt index 28d9236a..594da0c4 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/CalendarViewPresenter.kt @@ -1,65 +1,134 @@ package com.bodyweight.fitness.view import android.content.Context +import android.content.Intent +import android.support.v4.content.FileProvider import android.support.v4.view.ViewPager import android.support.v7.widget.LinearLayoutManager import android.util.AttributeSet import android.view.View +import android.widget.Toast +import com.bodyweight.fitness.Constants import com.bodyweight.fitness.R -import com.bodyweight.fitness.adapter.CalendarAdapter +import com.bodyweight.fitness.adapter.CalendarPagerAdapter import com.bodyweight.fitness.adapter.CalendarListAdapter -import com.bodyweight.fitness.extension.debug import com.bodyweight.fitness.isRoutineLoggedWithResults +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.repository.Repository import com.bodyweight.fitness.stream.Stream import com.trello.rxlifecycle.kotlin.bindToLifecycle + +import io.realm.RealmResults +import io.realm.Sort + import kotlinx.android.synthetic.main.view_calendar.view.* +import rx.Subscriber +import rx.android.schedulers.AndroidSchedulers + +import java.io.File +import java.io.FileOutputStream + class CalendarPresenter : AbstractPresenter() { @Transient - val mCalendarAdapter = CalendarAdapter() + val calendarAdapter = CalendarPagerAdapter() @Transient - val mCalendarListAdapter = CalendarListAdapter() + val calendarListAdapter = CalendarListAdapter() override fun bindView(view: AbstractView) { super.bindView(view) val view = (view as CalendarView) - view.view_calendar_pager.adapter = mCalendarAdapter - view.view_calendar_list.adapter = mCalendarListAdapter + view.view_calendar_pager.adapter = calendarAdapter + view.view_calendar_list.adapter = calendarListAdapter view.scrollToDefaultItem() - Stream.drawerObservable + Stream.menuObservable .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .filter { id -> id == R.id.action_menu_workout_log } + .filter { it == R.id.action_today } .subscribe { view.scrollToDefaultItem() } Stream.menuObservable .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .filter { it == R.id.action_today } + .filter { it == R.id.action_export } .subscribe { - view.scrollToDefaultItem() + Repository.realm.where(RepositoryRoutine::class.java) + .findAllAsync() + .sort("startTime", Sort.DESCENDING) + .asObservable() + .filter { it.isLoaded && Stream.currentDrawerId == R.id.action_menu_workout_log } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(getView()) + .subscribe(object: Subscriber>(){ + override fun onCompleted() {} + + override fun onError(e: Throwable) { + Toast.makeText(getView().context, "Error: Unable to export workout log", Toast.LENGTH_SHORT).show() + } + + override fun onNext(it: RealmResults) { + val context = getView().context + + val path = File(context.filesDir, "csv"); + val file = File(path, "LoggedWorkouts.csv").apply { + if (parentFile.mkdirs()) { + createNewFile() + } + } + + var content = "" + + for (repositoryRoutine: RepositoryRoutine in it) { + content += RepositoryRoutine.toCSV(repositoryRoutine) + } + + FileOutputStream(file).apply { + write(content.toByteArray()) + flush() + close() + } + + context.startActivity(Intent().apply { + action = Intent.ACTION_SEND + type = "text/csv" + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + + putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context, Constants.fileProvider, file)) + }) + } + }) + } + + Stream.repositoryObservable() + .bindToLifecycle(view) + .subscribe { + val results = Stream.currentCalendarDay + .getDate() + .isRoutineLoggedWithResults() + + if (results.isNotEmpty()) { + calendarListAdapter.setItems(results) + + view.showCardView() + } else { + view.hideCardView() + } } - Stream.calendarDayObservable + Stream.calendarDayObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { val results = it.getDate().isRoutineLoggedWithResults() if (results.isNotEmpty()) { - mCalendarListAdapter.setItems(results) + calendarListAdapter.setItems(results) view.showCardView() } else { @@ -69,12 +138,12 @@ class CalendarPresenter : AbstractPresenter() { } fun onPageSelected(position: Int) { - Stream.streamPage(position) + Stream.setCalendarPage(position) } } open class CalendarView : AbstractView { - override var mPresenter: AbstractPresenter = CalendarPresenter() + override var presenter: AbstractPresenter = CalendarPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -87,7 +156,7 @@ open class CalendarView : AbstractView { override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageSelected(position: Int) { - val presenter = (mPresenter as CalendarPresenter) + val presenter = (presenter as CalendarPresenter) presenter.onPageSelected(position) } @@ -105,6 +174,6 @@ open class CalendarView : AbstractView { } fun scrollToDefaultItem() { - view_calendar_pager.setCurrentItem(CalendarAdapter.DEFAULT_POSITION, false) + view_calendar_pager.setCurrentItem(CalendarPagerAdapter.DEFAULT_POSITION, false) } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/ContentViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/ContentViewPresenter.kt index f6c6b042..36d56fb5 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/ContentViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/ContentViewPresenter.kt @@ -2,77 +2,70 @@ package com.bodyweight.fitness.view import android.content.Context import android.util.AttributeSet -import android.view.View -import com.bodyweight.fitness.R -import com.bodyweight.fitness.extension.debug -import com.bodyweight.fitness.stream.RoutineStream +import com.bodyweight.fitness.R +import com.bodyweight.fitness.setGone +import com.bodyweight.fitness.setVisible import com.bodyweight.fitness.stream.Stream import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.activity_main.view.* -object ContentShared { - var id: Int = R.id.action_menu_home -} - class ContentPresenter : AbstractPresenter() { override fun bindView(view: AbstractView) { super.bindView(view) - Stream.drawerObservable + Stream.drawerObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .filter { - it.equals(R.id.action_menu_home) || it.equals(R.id.action_menu_workout_log) + it.equals(R.id.action_menu_home) + || it.equals(R.id.action_menu_workout) + || it.equals(R.id.action_menu_workout_log) } - .subscribe { id -> - ContentShared.id = id - - setContent(id) + .subscribe { + setContent(it) } - - RoutineStream.getInstance().routineChangedObservable.subscribe { routine -> - ContentShared.id = R.id.action_menu_home - - (mView as ContentView).showHome() - } } override fun restoreView(view: AbstractView) { super.restoreView(view) - setContent(ContentShared.id) + setContent(Stream.currentDrawerId) } fun setContent(id: Int) { val view: ContentView = (mView as ContentView) - if (id == R.id.action_menu_home) { - view.showHome() - } else if (id == R.id.action_menu_workout_log) { - view.showCalendar() + when (id) { + R.id.action_menu_home -> view.showHome() + R.id.action_menu_workout -> view.showWorkout() + R.id.action_menu_workout_log -> view.showCalendar() } } } open class ContentView : AbstractView { - override var mPresenter: AbstractPresenter = ContentPresenter() + override var presenter: AbstractPresenter = ContentPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - override fun onCreateView() {} - fun showHome() { - view_home.visibility = View.VISIBLE - view_calendar.visibility = View.GONE + view_home.setVisible() + view_workout.setGone() + view_calendar.setGone() + } + + fun showWorkout() { + view_home.setGone() + view_workout.setVisible() + view_calendar.setGone() } fun showCalendar() { - view_home.visibility = View.GONE - view_calendar.visibility = View.VISIBLE + view_home.setGone() + view_workout.setGone() + view_calendar.setVisible() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/HeaderViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/HeaderViewPresenter.kt index c392e4a4..8c7b0ac2 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/HeaderViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/HeaderViewPresenter.kt @@ -2,35 +2,36 @@ package com.bodyweight.fitness.view import android.content.Context import android.util.AttributeSet +import com.bodyweight.fitness.extension.debug import com.bodyweight.fitness.model.Routine import com.bodyweight.fitness.stream.RoutineStream import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.activity_main_header.view.* -import rx.Observable open class HeaderPresenter : AbstractPresenter() { - open fun getCurrentRoutine(): Routine = - RoutineStream.getInstance().routine - - open fun getRoutineObservable(): Observable = - RoutineStream.getInstance().routineObservable - override fun bindView(view: AbstractView) { super.bindView(view) - getRoutineObservable() - .bindToLifecycle(view) + debug("HeaderPresenter = bindView") + + /** + * We do not use bindView here as Navigation Drawer lifecycle is absolutely fucked. + */ + RoutineStream.routineObservable() .subscribe { setText(it) + debug("HeaderPresenter: Set title = " + it.title) } } override fun restoreView(view: AbstractView) { super.restoreView(view) - setText(getCurrentRoutine()) + debug("HeaderPresenter = restoreView") + + setText(RoutineStream.routine) } override fun getView(): HeaderView { @@ -43,7 +44,7 @@ open class HeaderPresenter : AbstractPresenter() { } open class HeaderView : AbstractView { - override var mPresenter: AbstractPresenter = HeaderPresenter() + override var presenter: AbstractPresenter = HeaderPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/HomeViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/HomeViewPresenter.kt new file mode 100644 index 00000000..714d68b7 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/HomeViewPresenter.kt @@ -0,0 +1,231 @@ +package com.bodyweight.fitness.view + +import android.content.Context +import android.content.Intent +import android.text.format.DateUtils +import android.util.AttributeSet + +import com.bodyweight.fitness.* +import com.bodyweight.fitness.model.RepositoryCategory +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.stream.RoutineStream +import com.bodyweight.fitness.stream.Stream +import com.bodyweight.fitness.ui.ProgressActivity + +import com.trello.rxlifecycle.kotlin.bindToLifecycle + +import kotlinx.android.synthetic.main.view_home.view.* +import kotlinx.android.synthetic.main.view_home_category.view.* + +import org.joda.time.DateTime + +class HomeViewPresenter : AbstractPresenter() { + override fun bindView(view: AbstractView) { + super.bindView(view) + + Stream.drawerObservable() + .bindToLifecycle(view) + .filter { it.equals(R.id.action_menu_home) } + .subscribe { + updateTodaysProgress() + updateStatistics() + } + + Stream.repositoryObservable() + .bindToLifecycle(view) + .subscribe { + updateTodaysProgress() + updateStatistics() + } + + RoutineStream.routineObservable() + .bindToLifecycle(view) + .subscribe { + updateTodaysProgress() + updateStatistics() + } + } + + fun updateTodaysProgress() { + val view = (getView() as HomeView) + + if (Repository.repositoryRoutineForTodayExists()) { + view.clearCategories() + + val repositoryRoutine = Repository.repositoryRoutineForToday + + for (category in repositoryRoutine.categories) { + val completionRate = RepositoryCategory.getCompletionRate(category) + + view.createCategory(category.title, completionRate.label, calculateLayoutWeight(completionRate.percentage)) + } + + val isRoutineCompleted = (RepositoryRoutine.getCompletionRate(repositoryRoutine).percentage == 100) + + view.setStartWorkoutButtonTitle(title = getStartWorkoutButtonText(true, isRoutineCompleted)) + view.showTodaysWorkoutLogButton() + } else { + view.clearCategories() + + val routine = RoutineStream.routine + + for (category in routine.categories) { + view.createCategory(category.title, "0%", calculateLayoutWeight(0)) + } + + view.setStartWorkoutButtonTitle(title = getStartWorkoutButtonText(false, false)) + view.hideTodaysWorkoutLogButton() + } + } + + fun updateStatistics() { + val view = (getView() as HomeView) + + val totalWorkouts = getNumberOfWorkouts() + val previousWorkoutLabel = getPreviousWorkoutLabel() + val last7Days = getNumberOfWorkouts(days = 7) + val last30Days = getNumberOfWorkouts(days = 30) + + view.setNumberOfWorkouts("$totalWorkouts ${getNumberOfWorkoutsPostfix(totalWorkouts)}") + view.setPreviousWorkout("$previousWorkoutLabel") + view.setNumberOfWorkoutsLast7Days("$last7Days ${getNumberOfWorkoutsPostfix(last7Days)}") + view.setNumberOfWorkoutsLast30Days("$last30Days ${getNumberOfWorkoutsPostfix(last30Days)}") + } + + private fun getStartWorkoutButtonText(repositoryRoutineForTodayExists: Boolean, isRoutineCompleted: Boolean): String { + if (repositoryRoutineForTodayExists) { + if (isRoutineCompleted) { + return "Review Workout" + } + + return "Go to Workout" + } + + return "Start Working Out" + } + + private fun getPreviousWorkoutLabel(): String { + val startTime = DateTime().withTimeAtStartOfDay() + + val results = Repository.realm.where(RepositoryRoutine::class.java) + .lessThan("startTime", startTime.toDate()) + .findAll() + + if (results.isNotEmpty()) { + results.lastOrNull()?.let { + return getRelativeTime(DateTime(it.startTime), System.currentTimeMillis()) + } + } + + return "Never" + } + + private fun getRelativeTime(time: DateTime, currentTime: Long): String { + return DateUtils.getRelativeTimeSpanString( + time.millis, + currentTime, + DateUtils.MINUTE_IN_MILLIS).toString() + } + + private fun getNumberOfWorkouts(): Int { + return Repository.realm.where(RepositoryRoutine::class.java) + .count() + .toInt() + } + + private fun getNumberOfWorkouts(days: Int = 7): Int { + val start = DateTime() + .withTimeAtStartOfDay() + .plusDays(1) + .minusSeconds(1) // 23:59 + + val end = start.minusDays(days) // 23:59 - 7 Days + + return Repository.realm.where(RepositoryRoutine::class.java) + .between("startTime", end.toDate(), start.toDate()) + .count() + .toInt() + } + + private fun getNumberOfWorkoutsPostfix(count: Int): String { + if (count == 1) { + return "Workout" + } else { + return "Workouts" + } + } + + fun startWorkout() { + Stream.setDrawer(R.id.action_menu_workout) + } + + fun todaysWorkoutLog() { + val routineId = Repository.repositoryRoutineForToday.id + + context().startActivity(Intent(context(), ProgressActivity::class.java) + .putExtra(Constants.primaryKeyRoutineId, routineId)) + } +} + +open class HomeView : AbstractView { + override var presenter: AbstractPresenter = HomeViewPresenter() + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + override fun onCreateView() { + super.onCreateView() + + start_workout.setOnClickListener { + (presenter as HomeViewPresenter).startWorkout() + } + + todays_workout_log.setOnClickListener { + (presenter as HomeViewPresenter).todaysWorkoutLog() + } + } + + fun setStartWorkoutButtonTitle(title: String) { + start_workout.text = title + } + + fun showTodaysWorkoutLogButton() { + todays_workout_log.setVisible() + } + + fun hideTodaysWorkoutLogButton() { + todays_workout_log.setGone() + } + + fun clearCategories() { + category.removeAllViews() + } + + fun createCategory(title: String, completionRateLabel: String, completionRateValue: Float) { + val new_category = category.inflate(R.layout.view_home_category) + + new_category.title.text = title + new_category.completion_rate_label.text = completionRateLabel + new_category.completion_rate_value.setLayoutWeight(completionRateValue) + + category.addView(new_category) + } + + fun setNumberOfWorkouts(title: String) { + total_workouts_value.text = title + } + + fun setPreviousWorkout(title: String) { + previous_workout_value.text = title + } + + fun setNumberOfWorkoutsLast7Days(title: String) { + last_7_days_value.text = title + } + + fun setNumberOfWorkoutsLast30Days(title: String) { + last_30_days_value.text = title + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/NavigationViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/NavigationViewPresenter.kt index 0ae8e2cf..f5ddb87c 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/NavigationViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/NavigationViewPresenter.kt @@ -2,9 +2,10 @@ package com.bodyweight.fitness.view import android.content.Context import android.util.AttributeSet -import android.view.View -import com.bodyweight.fitness.extension.debug +import com.bodyweight.fitness.setGone +import com.bodyweight.fitness.setInvisible +import com.bodyweight.fitness.setVisible import com.bodyweight.fitness.stream.RoutineStream import com.trello.rxlifecycle.kotlin.bindToLifecycle @@ -14,35 +15,42 @@ class NavigationPresenter : AbstractPresenter() { override fun bindView(view: AbstractView) { super.bindView(view) - getExerciseObservable() + RoutineStream.exerciseObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { val view = (view as NavigationView) - view.showOrHideButtons(it.isPrevious, it.isNext) view.showTimerOrRepsLogger(it.isTimedSet) + view.showPreviousNextButtons(it.isPrevious, it.isNext) } } override fun restoreView(view: AbstractView) { super.restoreView(view) - (view as NavigationView).showOrHideButtons(getCurrentExercise().isPrevious, getCurrentExercise().isNext) + val view = (view as NavigationView) + + val exercise = RoutineStream.exercise + + view.showTimerOrRepsLogger(exercise.isTimedSet) + view.showPreviousNextButtons(exercise.isPrevious, exercise.isNext) } fun previousExercise() { - RoutineStream.getInstance().setExercise(getCurrentExercise().previous) + if (RoutineStream.exercise.isPrevious) { + RoutineStream.exercise = RoutineStream.exercise.previous!! + } } fun nextExercise() { - RoutineStream.getInstance().setExercise(getCurrentExercise().next) + if (RoutineStream.exercise.isNext) { + RoutineStream.exercise = RoutineStream.exercise.next!! + } } } open class NavigationView : AbstractView { - override var mPresenter: AbstractPresenter = NavigationPresenter() + override var presenter: AbstractPresenter = NavigationPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -50,35 +58,35 @@ open class NavigationView : AbstractView { override fun onCreateView() { prev_exercise_button.setOnClickListener { - (mPresenter as NavigationPresenter).previousExercise() + (presenter as NavigationPresenter).previousExercise() } next_exercise_button.setOnClickListener { - (mPresenter as NavigationPresenter).nextExercise() + (presenter as NavigationPresenter).nextExercise() } } - fun showOrHideButtons(isPrevious: Boolean, isNext: Boolean) { - if (isPrevious) { - prev_exercise_button.visibility = View.VISIBLE + fun showTimerOrRepsLogger(isTimed: Boolean) { + if (isTimed) { + timer_view.setVisible() + reps_logger_view.setGone() } else { - prev_exercise_button.visibility = View.INVISIBLE + timer_view.setGone() + reps_logger_view.setVisible() } + } - if (isNext) { - next_exercise_button.visibility = View.VISIBLE + fun showPreviousNextButtons(hasPrevious: Boolean, hasNext: Boolean) { + if (hasPrevious) { + prev_exercise_button.setVisible() } else { - next_exercise_button.visibility = View.INVISIBLE + prev_exercise_button.setInvisible() } - } - fun showTimerOrRepsLogger(isTimed: Boolean) { - if (isTimed) { - timer_view.visibility = View.VISIBLE - reps_logger_view.visibility = View.GONE + if (hasNext) { + next_exercise_button.setVisible() } else { - timer_view.visibility = View.GONE - reps_logger_view.visibility = View.VISIBLE + next_exercise_button.setInvisible() } } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/PreviewViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/PreviewViewPresenter.kt index 41f35c7d..18fc8d09 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/PreviewViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/PreviewViewPresenter.kt @@ -1,51 +1,45 @@ package com.bodyweight.fitness.view import android.content.Context +import android.net.Uri import android.util.AttributeSet -import com.bodyweight.fitness.extension.debug -import com.bodyweight.fitness.model.Exercise -import com.bumptech.glide.Glide -import com.bumptech.glide.request.target.GlideDrawableImageViewTarget +import com.bodyweight.fitness.setInvisible +import com.bodyweight.fitness.setVisible + +import com.bodyweight.fitness.stream.RoutineStream import com.trello.rxlifecycle.kotlin.bindToLifecycle -import kotlinx.android.synthetic.main.view_preview.view.* -import rx.Subscriber +import kotlinx.android.synthetic.main.view_workout.view.* class PreviewPresenter : AbstractPresenter() { override fun bindView(view: AbstractView) { super.bindView(view) - getExerciseObservable() - .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } - .subscribe(object: Subscriber(){ - override fun onCompleted() {} - - override fun onError(e: Throwable) { - error("Glide Exception = " + e.message) - } - - override fun onNext(it: Exercise) { - val imageViewTarget = GlideDrawableImageViewTarget(view.image_view) - val identifier = view.context.resources.getIdentifier(it.id, "drawable", view.context.packageName) - - Glide.with(view.context) - .load(identifier) - .crossFade() - .into(imageViewTarget) - } - }) + RoutineStream.exerciseObservable().bindToLifecycle(view).subscribe { + if (it.videoId != "") { + view.video_view.setVisible() + + val identifier = view.context.resources.getIdentifier(it.videoId, "raw", view.context.packageName) + val videoUri = Uri.parse("android.resource://" + view.context.packageName + "/" + identifier); + + view.video_view.setVideoURI(videoUri); + view.video_view.setOnPreparedListener { + it.isLooping = true + + view.video_view.start() + } + } else { + view.video_view.setInvisible() + } + } } } open class PreviewView : AbstractView { - override var mPresenter: AbstractPresenter = PreviewPresenter() + override var presenter: AbstractPresenter = PreviewPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - override fun onCreateView() {} } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/RepsLoggerViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/RepsLoggerViewPresenter.kt index 95607ad5..1726aabe 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/RepsLoggerViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/RepsLoggerViewPresenter.kt @@ -2,33 +2,33 @@ package com.bodyweight.fitness.view import android.content.Context import android.util.AttributeSet -import com.bodyweight.fitness.Constants -import com.bodyweight.fitness.model.repository.RepositorySet -import com.bodyweight.fitness.stream.RepositoryStream -import com.bodyweight.fitness.stream.SetReps +import com.bodyweight.fitness.Constants +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.stream.RoutineStream import com.bodyweight.fitness.stream.Stream -import com.bodyweight.fitness.utils.PreferenceUtils -import com.trello.rxlifecycle.kotlin.bindToLifecycle +import com.bodyweight.fitness.utils.Preferences +import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.view_timer.view.* -import org.joda.time.DateTime import java.util.* class RepsLoggerPresenter : AbstractPresenter() { - var mNumberOfReps: Int = 5 + var numberOfReps: Int = 5 override fun bindView(view: AbstractView) { super.bindView(view) - getExerciseObservable() + RoutineStream.exerciseObservable() .bindToLifecycle(view) .subscribe { - mNumberOfReps = PreferenceUtils.getInstance().getNumberOfRepsForExercise(it.exerciseId, 5) + numberOfReps = Preferences.getNumberOfRepsForExercise(it.exerciseId, 5) + updateLabels() } - Stream.repositoryObservable + Stream.repositoryObservable() .bindToLifecycle(view) .subscribe { updateLabels() @@ -38,8 +38,7 @@ class RepsLoggerPresenter : AbstractPresenter() { override fun restoreView(view: AbstractView) { super.restoreView(view) - mNumberOfReps = PreferenceUtils.getInstance() - .getNumberOfRepsForExercise(getCurrentExercise().exerciseId, 5) + numberOfReps = Preferences.getNumberOfRepsForExercise(RoutineStream.exercise.exerciseId, 5) updateLabels() } @@ -48,50 +47,46 @@ class RepsLoggerPresenter : AbstractPresenter() { val repsLoggerView: RepsLoggerView = (mView as RepsLoggerView) repsLoggerView.setSets(formatSets()) - repsLoggerView.setNumberOfReps(formatNumberOfReps(mNumberOfReps)) + repsLoggerView.setNumberOfReps(formatNumberOfReps(numberOfReps)) - PreferenceUtils.getInstance().setNumberOfReps(getCurrentExercise().exerciseId, mNumberOfReps) + Preferences.setNumberOfReps(RoutineStream.exercise.exerciseId, numberOfReps) } fun logReps() { - val realm = RepositoryStream.getInstance().realm - val repositoryRoutine = RepositoryStream.getInstance().repositoryRoutineForToday + val realm = Repository.realm + val repositoryRoutine = Repository.repositoryRoutineForToday realm.executeTransaction { - val currentExercise = repositoryRoutine.exercises.filter { - it.exerciseId == getCurrentExercise().exerciseId - }.firstOrNull() - - if (currentExercise != null) { - val numberOfSets = currentExercise.sets.size + repositoryRoutine.exercises.filter { + it.exerciseId == RoutineStream.exercise.exerciseId + }.firstOrNull()?.let { + val numberOfSets = it.sets.size - if (numberOfSets < Constants.MAXIMUM_NUMBER_OF_SETS) { - val firstSet = currentExercise.sets.first() + if (numberOfSets < Constants.maximumNumberOfSets) { + val firstSet = it.sets.first() if (numberOfSets == 1 && firstSet.reps == 0) { - firstSet.reps = mNumberOfReps + firstSet.reps = numberOfReps - Stream.setLoggedSetReps(SetReps(numberOfSets, mNumberOfReps)) + Stream.setLoggedSetReps(SetReps(numberOfSets, numberOfReps)) } else { val repositorySet = realm.createObject(RepositorySet::class.java) repositorySet.id = "Set-" + UUID.randomUUID().toString() - repositorySet.setIsTimed(false) + repositorySet.isTimed = false repositorySet.seconds = 0 repositorySet.weight = 0.0 - repositorySet.reps = mNumberOfReps + repositorySet.reps = numberOfReps - repositorySet.exercise = currentExercise + repositorySet.exercise = it - currentExercise.sets.add(repositorySet) + it.sets.add(repositorySet) Stream.setRepository() - Stream.setLoggedSetReps(SetReps(numberOfSets + 1, mNumberOfReps)) + Stream.setLoggedSetReps(SetReps(numberOfSets + 1, numberOfReps)) } - repositoryRoutine.lastUpdatedTime = DateTime().toDate() - - realm.copyToRealmOrUpdate(repositoryRoutine) + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = true) } } } @@ -100,35 +95,35 @@ class RepsLoggerPresenter : AbstractPresenter() { } fun increaseReps() { - if (mNumberOfReps < 25) { - mNumberOfReps += 1 + if (numberOfReps < 25) { + numberOfReps += 1 updateLabels() } } fun decreaseReps() { - if (mNumberOfReps > 1) { - mNumberOfReps -= 1 + if (numberOfReps > 1) { + numberOfReps -= 1 updateLabels() } } fun formatSets(): String { - val exists = RepositoryStream.getInstance().repositoryRoutineForTodayExists() + val exists = Repository.repositoryRoutineForTodayExists() if (exists) { - val routine = RepositoryStream.getInstance().repositoryRoutineForToday + val routine = Repository.repositoryRoutineForToday routine.exercises.filter { - it.exerciseId == getCurrentExercise().exerciseId - }.first()?.let { + it.exerciseId == RoutineStream.exercise.exerciseId + }.firstOrNull()?.let { val sets = it.sets if (sets.size == 1 && sets.first().reps == 0) { return "First Set" - } else if (sets.size >= Constants.MAXIMUM_NUMBER_OF_SETS) { + } else if (sets.size >= Constants.maximumNumberOfSets) { return "12 Sets" } else if (sets.size >= 5) { return "Set ${sets.count() + 1}" @@ -161,7 +156,7 @@ class RepsLoggerPresenter : AbstractPresenter() { } open class RepsLoggerView : AbstractView { - override var mPresenter: AbstractPresenter = RepsLoggerPresenter() + override var presenter: AbstractPresenter = RepsLoggerPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -170,7 +165,7 @@ open class RepsLoggerView : AbstractView { override fun onFinishInflate() { super.onFinishInflate() - val repsLoggerPresenter: RepsLoggerPresenter = (mPresenter as RepsLoggerPresenter) + val repsLoggerPresenter: RepsLoggerPresenter = (presenter as RepsLoggerPresenter) log_reps_button.setOnClickListener { repsLoggerPresenter.logReps() @@ -185,8 +180,6 @@ open class RepsLoggerView : AbstractView { } } - override fun onCreateView() {} - fun setSets(sets: String) { reps_logger_sets.text = sets } diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/TimerViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/TimerViewPresenter.kt index 116645d6..71f809a2 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/TimerViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/TimerViewPresenter.kt @@ -7,54 +7,51 @@ import android.os.CountDownTimer import android.util.AttributeSet import com.bodyweight.fitness.* -import com.bodyweight.fitness.extension.* -import com.bodyweight.fitness.model.repository.RepositoryExercise -import com.bodyweight.fitness.model.repository.RepositorySet -import com.bodyweight.fitness.stream.RepositoryStream +import com.bodyweight.fitness.model.RepositoryRoutine +import com.bodyweight.fitness.model.RepositorySet +import com.bodyweight.fitness.repository.Repository import com.bodyweight.fitness.stream.RoutineStream import com.bodyweight.fitness.stream.Stream -import com.bodyweight.fitness.utils.PreferenceUtils +import com.bodyweight.fitness.utils.Preferences import com.trello.rxlifecycle.kotlin.bindToLifecycle import kotlinx.android.synthetic.main.view_timer.view.* -import org.joda.time.DateTime import java.util.* object TimerShared { - var mSeconds = 60 - var mCurrentSeconds = mSeconds - var mStartedLoggingSeconds = mSeconds - var mLoggedSeconds = 0 + var seconds = 60 + var currentSeconds = seconds + var startedLoggingSeconds = seconds + var loggedSeconds = 0 - var mPlaying = false - var mRestored = false + var isPlaying = false + var restored = false - var mCountDownTimer: CountDownTimer? = null + var countDownTimer: CountDownTimer? = null } class TimerPresenter : AbstractPresenter() { override fun bindView(view: AbstractView) { super.bindView(view) - Stream.drawerObservable + Stream.drawerObservable() .bindToLifecycle(view) + .filter { !it.equals(R.id.action_menu_workout) } .subscribe { pauseTimer() } - getExerciseObservable() + RoutineStream.exerciseObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .subscribe { - TimerShared.mCountDownTimer?.cancel() + TimerShared.countDownTimer?.cancel() - if (TimerShared.mRestored) { - TimerShared.mRestored = false + if (TimerShared.restored) { + TimerShared.restored = false - restartTimer(TimerShared.mCurrentSeconds, true, TimerShared.mPlaying) + restartTimer(TimerShared.currentSeconds, true, TimerShared.isPlaying) - if (TimerShared.mPlaying) { + if (TimerShared.isPlaying) { startTimer() } } else { @@ -64,7 +61,7 @@ class TimerPresenter : AbstractPresenter() { } override fun saveView() { - TimerShared.mRestored = true + TimerShared.restored = true super.saveView() } @@ -72,52 +69,52 @@ class TimerPresenter : AbstractPresenter() { fun increaseTimer(extraSeconds: Int) { val view = (mView as TimerView) - if (TimerShared.mPlaying) { - TimerShared.mCountDownTimer?.cancel() + if (TimerShared.isPlaying) { + TimerShared.countDownTimer?.cancel() - TimerShared.mCurrentSeconds += extraSeconds - TimerShared.mCountDownTimer = buildCountDownTimer(TimerShared.mCurrentSeconds, false, true) + TimerShared.currentSeconds += extraSeconds + TimerShared.countDownTimer = buildCountDownTimer(TimerShared.currentSeconds, false, true) - view.setMinutes(TimerShared.mCurrentSeconds.formatMinutes()) - view.setSeconds(TimerShared.mCurrentSeconds.formatSeconds()) + view.setMinutes(TimerShared.currentSeconds.formatMinutes()) + view.setSeconds(TimerShared.currentSeconds.formatSeconds()) - TimerShared.mCountDownTimer?.start() + TimerShared.countDownTimer?.start() } else { - TimerShared.mCurrentSeconds += extraSeconds - TimerShared.mCountDownTimer = buildCountDownTimer(TimerShared.mCurrentSeconds, false, true) + TimerShared.currentSeconds += extraSeconds + TimerShared.countDownTimer = buildCountDownTimer(TimerShared.currentSeconds, false, true) - view.setMinutes(TimerShared.mCurrentSeconds.formatMinutes()) - view.setSeconds(TimerShared.mCurrentSeconds.formatSeconds()) + view.setMinutes(TimerShared.currentSeconds.formatMinutes()) + view.setSeconds(TimerShared.currentSeconds.formatSeconds()) } } fun pauseTimer() { val view = (mView as TimerView) - TimerShared.mCountDownTimer?.cancel() + TimerShared.countDownTimer?.cancel() - TimerShared.mPlaying = false - TimerShared.mCountDownTimer = buildCountDownTimer(TimerShared.mCurrentSeconds, false, false) + TimerShared.isPlaying = false + TimerShared.countDownTimer = buildCountDownTimer(TimerShared.currentSeconds, false, false) - view.setMinutes(TimerShared.mCurrentSeconds.formatMinutes()) - view.setSeconds(TimerShared.mCurrentSeconds.formatSeconds()) + view.setMinutes(TimerShared.currentSeconds.formatMinutes()) + view.setSeconds(TimerShared.currentSeconds.formatSeconds()) view.showPaused() } fun startTimer() { - TimerShared.mCountDownTimer?.start() + TimerShared.countDownTimer?.start() } fun restartTimer(seconds: Int, restored: Boolean, isPlaying: Boolean) { val view = (mView as TimerView) - TimerShared.mCountDownTimer?.cancel() + TimerShared.countDownTimer?.cancel() - TimerShared.mPlaying = isPlaying - TimerShared.mCurrentSeconds = seconds + TimerShared.isPlaying = isPlaying + TimerShared.currentSeconds = seconds - TimerShared.mCountDownTimer = buildCountDownTimer(seconds, restored, false) + TimerShared.countDownTimer = buildCountDownTimer(seconds, restored, false) view.setMinutes(seconds.formatMinutes()) view.setSeconds(seconds.formatSeconds()) @@ -128,7 +125,7 @@ class TimerPresenter : AbstractPresenter() { fun playSound() { val view = (mView as TimerView) - if (PreferenceUtils.getInstance().playSoundWhenTimerStops()) { + if (Preferences.playSoundWhenTimerStops()) { val mediaPlayer = MediaPlayer.create(view.context, R.raw.finished) mediaPlayer.isLooping = false @@ -142,20 +139,20 @@ class TimerPresenter : AbstractPresenter() { pauseTimer() val timePickerDialog = TimePickerDialog(view.context, { view, minutes, seconds -> - TimerShared.mCurrentSeconds = seconds + minutes * 60 + TimerShared.currentSeconds = seconds + minutes * 60 - if (TimerShared.mCurrentSeconds < 10) { - TimerShared.mCurrentSeconds = 10 + if (TimerShared.currentSeconds < 10) { + TimerShared.currentSeconds = 10 } - restartTimer(TimerShared.mCurrentSeconds, false, false) + restartTimer(TimerShared.currentSeconds, false, false) - TimerShared.mSeconds = TimerShared.mCurrentSeconds + TimerShared.seconds = TimerShared.currentSeconds - val save = (TimerShared.mSeconds * 1000).toLong() + val save = (TimerShared.seconds * 1000).toLong() - PreferenceUtils.getInstance().setTimerValue(getCurrentExercise().exerciseId, save) - }, TimerShared.mCurrentSeconds.formatMinutesAsNumber(), TimerShared.mCurrentSeconds.formatSecondsAsNumber(), true) + Preferences.setTimerValue(RoutineStream.exercise.exerciseId, save) + }, TimerShared.currentSeconds.formatMinutesAsNumber(), TimerShared.currentSeconds.formatSecondsAsNumber(), true) timePickerDialog.show() } @@ -165,7 +162,7 @@ class TimerPresenter : AbstractPresenter() { } fun onClickStartStopTimeButton() { - if (TimerShared.mPlaying) { + if (TimerShared.isPlaying) { logTime() pauseTimer() @@ -184,13 +181,13 @@ class TimerPresenter : AbstractPresenter() { val view = (mView as TimerView) if (increaseTimer) { - TimerShared.mLoggedSeconds += 5 + TimerShared.loggedSeconds += 5 } else { if (restored) { - TimerShared.mLoggedSeconds = TimerShared.mStartedLoggingSeconds + TimerShared.loggedSeconds = TimerShared.startedLoggingSeconds } else { - TimerShared.mStartedLoggingSeconds = seconds - TimerShared.mLoggedSeconds = seconds + TimerShared.startedLoggingSeconds = seconds + TimerShared.loggedSeconds = seconds } } @@ -198,8 +195,8 @@ class TimerPresenter : AbstractPresenter() { override fun onTick(millisUntilFinished: Long) { val timerSeconds = millisUntilFinished.toInt() / 1000 - TimerShared.mPlaying = true - TimerShared.mCurrentSeconds = timerSeconds + TimerShared.isPlaying = true + TimerShared.currentSeconds = timerSeconds view.setMinutes(timerSeconds.formatMinutes()) view.setSeconds(timerSeconds.formatSeconds()) @@ -218,14 +215,12 @@ class TimerPresenter : AbstractPresenter() { } fun getSeconds(): Int { - return (PreferenceUtils.getInstance() - .getTimerValueForExercise(getCurrentExercise().exerciseId, 60 * 1000) / 1000 - ).toInt() + return (Preferences.getTimerValueForExercise(RoutineStream.exercise.exerciseId, 60 * 1000) / 1000).toInt() } fun logTime() { - if (PreferenceUtils.getInstance().automaticallyLogWorkoutTime() && getCurrentExercise().isTimedSet) { - val loggedSeconds = TimerShared.mLoggedSeconds - TimerShared.mCurrentSeconds + if (Preferences.automaticallyLogWorkoutTime() && RoutineStream.exercise.isTimedSet) { + val loggedSeconds = TimerShared.loggedSeconds - TimerShared.currentSeconds if (loggedSeconds > 0) { if (logIntoRealm(loggedSeconds)) { @@ -236,71 +231,51 @@ class TimerPresenter : AbstractPresenter() { } private fun logIntoRealm(logSeconds: Int): Boolean { - // getRepositoryRoutineForToday method - begins realm transaction. - val repositoryRoutine = RepositoryStream.getInstance().repositoryRoutineForToday - var mRepositoryExercise: RepositoryExercise? = null + val realm = Repository.realm + val repositoryRoutine = Repository.repositoryRoutineForToday - val realm = RepositoryStream.getInstance().realm - val exercise = RoutineStream.getInstance().exercise + var isLogged: Boolean = false - realm.beginTransaction() - for (repositoryExercise in repositoryRoutine.exercises) { - if (repositoryExercise.title == exercise.title) { - mRepositoryExercise = repositoryExercise + realm.executeTransaction { + repositoryRoutine.exercises + .filter { it.exerciseId == RoutineStream.exercise.exerciseId } + .firstOrNull()?.let { - break - } - } + val numberOfSets = it.sets.size + if (numberOfSets < Constants.maximumNumberOfSets) { + val firstSet = it.sets.first() - if (mRepositoryExercise != null) { - // if there is already a set which is timed and has 0 seconds then overwrite the values. - // otherwise create new one. + if (numberOfSets == 1 && firstSet.isTimed && firstSet.seconds == 0) { + if (firstSet.isTimed && firstSet.seconds == 0) { + firstSet.seconds = logSeconds + } + } else { + val repositorySet = realm.createObject(RepositorySet::class.java) - val numberOfSets = mRepositoryExercise.sets.size + repositorySet.id = "Set-" + UUID.randomUUID().toString() + repositorySet.isTimed = true + repositorySet.seconds = logSeconds + repositorySet.weight = 0.0 + repositorySet.reps = 0 - if (numberOfSets >= Constants.MAXIMUM_NUMBER_OF_SETS) { - realm.cancelTransaction() + repositorySet.exercise = it - return false - } + it.sets.add(repositorySet) + } - if (numberOfSets == 1 && mRepositoryExercise.sets[0].isTimed && mRepositoryExercise.sets[0].seconds == 0) { - val firstSet = mRepositoryExercise.sets[0] + RepositoryRoutine.setLastUpdatedTime(repositoryRoutine, isNestedTransaction = true) - if (firstSet.isTimed && firstSet.seconds == 0) { - firstSet.seconds = logSeconds + isLogged = true } - } else { - val repositorySet = realm.createObject(RepositorySet::class.java) - - repositorySet.id = "Set-" + UUID.randomUUID().toString() - repositorySet.setIsTimed(true) - repositorySet.seconds = logSeconds - repositorySet.weight = 0.0 - repositorySet.reps = 0 - - repositorySet.exercise = mRepositoryExercise - - mRepositoryExercise.sets.add(repositorySet) } - - // TODO: Unit test the fact that lastUpdatedTime is changed when logging sets from main UI. - repositoryRoutine.lastUpdatedTime = DateTime().toDate() - - realm.copyToRealmOrUpdate(repositoryRoutine) - realm.commitTransaction() - - return true - } else { - realm.cancelTransaction() - - return false } + + return isLogged } } open class TimerView : AbstractView { - override var mPresenter: AbstractPresenter = TimerPresenter() + override var presenter: AbstractPresenter = TimerPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -309,7 +284,7 @@ open class TimerView : AbstractView { override fun onFinishInflate() { super.onFinishInflate() - val presenter = (mPresenter as TimerPresenter) + val presenter = (presenter as TimerPresenter) timer_layout.setOnClickListener { presenter.onClickTimeLayout() } increase_timer_button.setOnClickListener { presenter.onClickIncreaseTimeButton() } @@ -317,8 +292,6 @@ open class TimerView : AbstractView { restart_timer_button.setOnClickListener { presenter.onClickRestartTimeButton() } } - override fun onCreateView() { } - fun setMinutes(text: String) { timer_minutes.text = text } diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/ToolbarViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/ToolbarViewPresenter.kt index 95b5d291..75efd004 100644 --- a/app/src/main/kotlin/com/bodyweight/fitness/view/ToolbarViewPresenter.kt +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/ToolbarViewPresenter.kt @@ -2,11 +2,17 @@ package com.bodyweight.fitness.view import android.content.Context import android.util.AttributeSet +import android.view.View +import android.widget.Adapter +import android.widget.AdapterView +import android.widget.Toast import com.bodyweight.fitness.R -import com.bodyweight.fitness.extension.debug +import com.bodyweight.fitness.adapter.ToolbarSpinnerAdapter import com.bodyweight.fitness.model.CalendarDay import com.bodyweight.fitness.model.Exercise +import com.bodyweight.fitness.setGone +import com.bodyweight.fitness.setVisible import com.bodyweight.fitness.stream.RoutineStream import com.bodyweight.fitness.stream.Stream @@ -17,42 +23,32 @@ import org.joda.time.DateTime import java.util.* -object ToolbarPresenterState { - var id: Int = R.id.action_menu_home -} - class ToolbarPresenter : AbstractPresenter() { override fun bindView(view: AbstractView) { super.bindView(view) - getExerciseObservable() - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } + RoutineStream.exerciseObservable() .bindToLifecycle(view) - .filter { ToolbarPresenterState.id.equals(R.id.action_menu_home) } + .filter { Stream.currentDrawerId.equals(R.id.action_menu_workout) } .subscribe { - setToolbarForHome(it) + setToolbarForWorkout(it) } - Stream.calendarDayObservable - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } + Stream.calendarDayObservable() .bindToLifecycle(view) - .filter { ToolbarPresenterState.id.equals(R.id.action_menu_workout_log) } + .filter { Stream.currentDrawerId.equals(R.id.action_menu_workout_log) } .subscribe { setToolbarForWorkoutLog(it) } - Stream.drawerObservable + Stream.drawerObservable() .bindToLifecycle(view) - .doOnSubscribe { debug(this.javaClass.simpleName + " = doOnSubscribe") } - .doOnUnsubscribe { debug(this.javaClass.simpleName + " = doOnUnsubscribe") } .filter { - it.equals(R.id.action_menu_home) || it.equals(R.id.action_menu_workout_log) + it.equals(R.id.action_menu_home) + || it.equals(R.id.action_menu_workout) + || it.equals(R.id.action_menu_workout_log) } .subscribe { - ToolbarPresenterState.id = it - setToolbar() } } @@ -64,25 +60,67 @@ class ToolbarPresenter : AbstractPresenter() { } fun setToolbar() { - when (ToolbarPresenterState.id) { - R.id.action_menu_home -> - setToolbarForHome(RoutineStream.getInstance().exercise) - R.id.action_menu_workout_log -> + when (Stream.currentDrawerId) { + R.id.action_menu_home -> { + setToolbarForHome() + } + + R.id.action_menu_workout -> { + setToolbarForWorkout(RoutineStream.exercise) + } + + R.id.action_menu_workout_log -> { setToolbarForWorkoutLog(Stream.currentCalendarDay) + } } } - fun setToolbarForHome(exercise: Exercise) { - val view: ToolbarView = (mView as ToolbarView) + private fun setToolbarForHome() { + val toolbarView: ToolbarView = (mView as ToolbarView) + + val routine = RoutineStream.routine + + toolbarView.setSpinner(routine.title, routine.subtitle) + + var isAdapterCreated = false + val spinnerAdapter = ToolbarSpinnerAdapter() + + toolbarView.toolbar_spinner.adapter = spinnerAdapter + toolbarView.toolbar_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View?, pos: Int, id: Long) { + if (isAdapterCreated) { + val spinnerRoutine = spinnerAdapter.routines[pos] + + RoutineStream.setRoutine(spinnerRoutine) + + toolbarView.setSpinner(spinnerRoutine.title, spinnerRoutine.subtitle) - view.inflateHomeMenu() + view?.let { + Toast.makeText(it.context, + "Switched routine to ${spinnerRoutine.title}", + Toast.LENGTH_LONG + ).show() + } + } else { + isAdapterCreated = true + } + } + + override fun onNothingSelected(parent: AdapterView?) { + + } + } + } + + private fun setToolbarForWorkout(exercise: Exercise) { + val view: ToolbarView = (mView as ToolbarView) view.setTitle(exercise.title) - view.setSubtitle(exercise.section.title) + view.setSubtitle(exercise.section!!.title) view.setDescription(exercise.description) } - fun setToolbarForWorkoutLog(calendarDay: CalendarDay?) { + private fun setToolbarForWorkoutLog(calendarDay: CalendarDay?) { if (calendarDay == null) { setDateTimeSingleTitle(DateTime()) } else { @@ -93,47 +131,54 @@ class ToolbarPresenter : AbstractPresenter() { private fun setDateTimeSingleTitle(dateTime: DateTime) { val view: ToolbarView = (mView as ToolbarView) - view.inflateWorkoutLogMenu() view.setSingleTitle(dateTime.toString("dd MMMM, YYYY", Locale.ENGLISH)) } } class ToolbarView : AbstractView { - override var mPresenter: AbstractPresenter = ToolbarPresenter() + override var presenter: AbstractPresenter = ToolbarPresenter() constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - override fun onCreateView() {} + fun setSpinner(title: String, subtitle: String) { + toolbar_spinner_layout.setVisible() + toolbar_layout.setGone() - fun inflateHomeMenu() { - toolbar.menu.clear() - toolbar.inflateMenu(R.menu.home) - } + toolbar_spinner_title.text = title + toolbar_spinner_subtitle.text = subtitle - fun inflateWorkoutLogMenu() { - toolbar.menu.clear() - toolbar.inflateMenu(R.menu.calendar) + toolbar.title = "" + toolbar.subtitle = "" } fun setSingleTitle(text: String) { - toolbar_layout.visibility = GONE + toolbar_spinner_layout.setGone() + toolbar_layout.setGone() + toolbar.title = text + toolbar.subtitle = "" } fun setTitle(text: String) { - toolbar_layout.visibility = VISIBLE + toolbar_spinner_layout.setGone() + toolbar_layout.setVisible() + toolbar_exercise_title.text = text } fun setSubtitle(text: String) { - toolbar_layout.visibility = VISIBLE + toolbar_spinner_layout.setGone() + toolbar_layout.setVisible() + toolbar_section_title.text = text } fun setDescription(text: String) { - toolbar_layout.visibility = VISIBLE + toolbar_spinner_layout.setGone() + toolbar_layout.setVisible() + toolbar_exercise_description.text = text } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/WorkoutViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/WorkoutViewPresenter.kt new file mode 100644 index 00000000..710c59d4 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/WorkoutViewPresenter.kt @@ -0,0 +1,101 @@ +package com.bodyweight.fitness.view + +import android.content.Context +import android.support.design.widget.TabLayout +import android.util.AttributeSet + +import com.bodyweight.fitness.model.WorkoutViewType +import com.bodyweight.fitness.setGone +import com.bodyweight.fitness.setVisible +import com.bodyweight.fitness.stream.RoutineStream +import com.bodyweight.fitness.stream.Stream + +import com.trello.rxlifecycle.kotlin.bindToLifecycle + +import kotlinx.android.synthetic.main.view_timer.view.* +import kotlinx.android.synthetic.main.view_workout.view.* + +class WorkoutViewPresenter : AbstractPresenter() { + override fun bindView(view: AbstractView) { + super.bindView(view) + + RoutineStream.exerciseObservable() + .bindToLifecycle(view) + .subscribe { + (view as WorkoutView).showHideViewTabs(it.isTimedSet) + } + } +} + +open class WorkoutView : AbstractView { + override var presenter: AbstractPresenter = WorkoutViewPresenter() + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + override fun onCreateView() { + super.onCreateView() + + view_tabs.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + when (tab.text) { + "Reps Logger" -> { + Stream.setWorkoutView(WorkoutViewType.RepsLogger) + + timer_view.setGone() + reps_logger_view.setVisible() + } + else -> { + Stream.setWorkoutView(WorkoutViewType.Timer) + + timer_view.setVisible() + reps_logger_view.setGone() + } + } + } + + override fun onTabUnselected(tab: TabLayout.Tab) { } + override fun onTabReselected(tab: TabLayout.Tab) { } + }) + + showHideViewTabs(RoutineStream.exercise.isTimedSet) + } + + fun showHideViewTabs(isTimed: Boolean) { + if (isTimed) { + if (view_tabs.tabCount != 1 || view_tabs.getTabAt(0)!!.text != "Timer") { + view_tabs.removeAllTabs() + + addTab("Timer") + } + + selectFirstTab() + + Stream.setWorkoutView(WorkoutViewType.Timer) + } else { + if (view_tabs.tabCount != 2 || view_tabs.getTabAt(1)!!.text != "Reps Logger") { + view_tabs.removeAllTabs() + + addTab("Timer") + addTab("Reps Logger") + } + + selectSecondTab() + + Stream.setWorkoutView(WorkoutViewType.RepsLogger) + } + } + + fun selectFirstTab() { + view_tabs.getTabAt(0)?.select() + } + + fun selectSecondTab() { + view_tabs.getTabAt(1)?.select() + } + + fun addTab(title: String) { + view_tabs.addTab(view_tabs.newTab().setText(title)) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/bodyweight/fitness/view/progress/ProgressGeneralViewPresenter.kt b/app/src/main/kotlin/com/bodyweight/fitness/view/progress/ProgressGeneralViewPresenter.kt new file mode 100644 index 00000000..7d9767b9 --- /dev/null +++ b/app/src/main/kotlin/com/bodyweight/fitness/view/progress/ProgressGeneralViewPresenter.kt @@ -0,0 +1,358 @@ +package com.bodyweight.fitness.view.progress + +import android.content.Context +import android.graphics.Color +import android.support.design.widget.TabLayout +import android.util.AttributeSet + +import com.bodyweight.fitness.* +import com.bodyweight.fitness.adapter.CompletionRateAdapter +import com.bodyweight.fitness.adapter.WorkoutLengthAdapter +import com.bodyweight.fitness.model.* +import com.bodyweight.fitness.repository.Repository +import com.bodyweight.fitness.view.AbstractPresenter +import com.bodyweight.fitness.view.AbstractView + +import com.trello.rxlifecycle.kotlin.bindToLifecycle + +import io.realm.Sort + +import kotlinx.android.synthetic.main.activity_progress_general.view.* +import kotlinx.android.synthetic.main.activity_progress_general_exercise.view.* +import kotlinx.android.synthetic.main.view_home_category.view.* + +import org.joda.time.DateTime +import rx.android.schedulers.AndroidSchedulers + +import java.util.* + +import kotlin.properties.Delegates + +class ProgressGeneralViewPresenter : AbstractPresenter() { + var repositoryRoutine: RepositoryRoutine by Delegates.notNull() + + val exercises by lazy { + RepositoryRoutine.getVisibleAndCompletedExercises(repositoryRoutine.exercises) + } + + val missedExercises by lazy { + RepositoryRoutine.getMissedExercises(repositoryRoutine.exercises) + } + + val numberOfExercises by lazy { + RepositoryRoutine.getNumberOfExercises(exercises) + } + + val numberOfCompletedExercises by lazy { + RepositoryRoutine.getNumberOfCompletedExercises(exercises) + } + + val routineCompletionRate by lazy { + RepositoryRoutine.getCompletionRate(repositoryRoutine) + } + + override fun updateView() { + super.updateView() + + renderTime() + renderTodaysProgress() + renderMissedExercises() + renderWorkoutLengthHistoryGraph() + renderCompletionRateHistoryGraph() + } + + fun renderTime() { + val view = getView() as ProgressGeneralView + + view.start_time_value.text = RepositoryRoutine.getStartTime(repositoryRoutine) + view.end_time_value.text = RepositoryRoutine.getLastUpdatedTime(repositoryRoutine) + view.workout_length_value.text = RepositoryRoutine.getWorkoutLength(repositoryRoutine) + + if (routineCompletionRate.percentage == 100) { + view.end_time_label.text = "End Time" + } else { + view.end_time_label.text = "Last Updated" + } + } + + fun renderTodaysProgress() { + val view = getView() as ProgressGeneralView + + view.general_completed_exercises_value.text = "$numberOfCompletedExercises out of $numberOfExercises" + view.general_completion_rate_value.text = "${routineCompletionRate.percentage}%" + + view.clearCategories() + + for (category in repositoryRoutine.categories) { + val completionRate = RepositoryCategory.getCompletionRate(category) + + view.createCategory(category.title, completionRate.label, calculateLayoutWeight(completionRate.percentage)) + } + } + + fun renderMissedExercises() { + val view = getView() as ProgressGeneralView + val parent = view.missed_exercises_layout + + if (missedExercises.isNotEmpty()) { + view.missed_exercises_title.setVisible() + view.missed_exercises_card.setVisible() + + for (exercise in missedExercises) { + val layout = parent.inflate(R.layout.activity_progress_general_exercise) + + layout.exercise_title.text = exercise.title + layout.category_title.text = exercise.category?.title + + parent.addView(layout) + } + } else { + view.missed_exercises_title.setGone() + view.missed_exercises_card.setGone() + } + } + + fun renderWorkoutLengthHistoryGraph() { + val view = getView() as ProgressGeneralView + + val workoutLengthGraphView = view.graph_workout_length_view + val workoutLengthTabLayout = view.graph_workout_length_tablayout + + val workoutLengthAdapter = WorkoutLengthAdapter() + + workoutLengthGraphView.adapter = workoutLengthAdapter + workoutLengthGraphView.baseLineColor = Color.WHITE + workoutLengthGraphView.scrubLineColor = Color.parseColor("#111111") + workoutLengthGraphView.isScrubEnabled = true + workoutLengthGraphView.animateChanges = true + + workoutLengthGraphView.setScrubListener { + val dateTimeWorkoutLength = it as? DateTimeWorkoutLength + + dateTimeWorkoutLength?.let { + view.graph_workout_length_title.text = it.dateTime.toString("dd MMMM, YYYY", Locale.ENGLISH) + + if (it.repositoryRoutine != null) { + view.graph_workout_length_value.text = RepositoryRoutine.getWorkoutLength(it.repositoryRoutine) + } else { + view.graph_workout_length_value.text = "Not Completed" + } + } + } + + workoutLengthTabLayout.addTab(workoutLengthTabLayout.newTab().setText("1W")) + workoutLengthTabLayout.addTab(workoutLengthTabLayout.newTab().setText("1M")) + workoutLengthTabLayout.addTab(workoutLengthTabLayout.newTab().setText("3M")) + workoutLengthTabLayout.addTab(workoutLengthTabLayout.newTab().setText("6M")) + workoutLengthTabLayout.addTab(workoutLengthTabLayout.newTab().setText("1Y")) + + workoutLengthTabLayout.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + updateWorkoutLengthTitle() + + when (tab.position) { + 0 -> updateWorkoutLengthGraph(workoutLengthAdapter, 7) + 1 -> updateWorkoutLengthGraph(workoutLengthAdapter, 30) + 2 -> updateWorkoutLengthGraph(workoutLengthAdapter, 90) + 3 -> updateWorkoutLengthGraph(workoutLengthAdapter, 180) + else -> updateWorkoutLengthGraph(workoutLengthAdapter, 360) + } + } + + override fun onTabUnselected(tab: TabLayout.Tab) { + + } + + override fun onTabReselected(tab: TabLayout.Tab) { + + } + }) + + updateWorkoutLengthGraph(workoutLengthAdapter, 7) + updateWorkoutLengthTitle() + } + + fun updateWorkoutLengthTitle() { + val view = getView() as ProgressGeneralView + + view.graph_workout_length_title.text = DateTime(repositoryRoutine.startTime).toString("dd MMMM, YYYY", Locale.ENGLISH) + view.graph_workout_length_value.text = "${RepositoryRoutine.getWorkoutLength(repositoryRoutine)}" + } + + fun updateWorkoutLengthGraph(adapter: WorkoutLengthAdapter, minusDays: Int = 7) { + val start = DateTime.now().withTimeAtStartOfDay().minusDays(minusDays) + val end = DateTime.now() + + Repository.realm.where(RepositoryRoutine::class.java) + .between("startTime", start.toDate(), end.toDate()) + .findAllAsync() + .sort("startTime", Sort.DESCENDING) + .asObservable() + .filter { it.isLoaded } + .map { + val dates = ArrayList() + + for (index in 1..minusDays) { + val date = start.plusDays(index) + + val repositoryRoutine = it.filter { + val startTime = DateTime(it.startTime) + + date.dayOfMonth == startTime.dayOfMonth + && date.monthOfYear == startTime.monthOfYear + && date.year == startTime.year + }.firstOrNull() + + if (repositoryRoutine != null) { + dates.add(DateTimeWorkoutLength(date, repositoryRoutine)) + } else { + dates.add(DateTimeWorkoutLength(date, null)) + } + } + + dates + } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(getView()) + .subscribe { + adapter.changeData(it) + } + } + + fun renderCompletionRateHistoryGraph() { + val view = getView() as ProgressGeneralView + + val completionRateGraphView = view.graph_completion_rate_view + val completionRateTabLayout = view.graph_completion_rate_tablayout + + val completionRateAdapter = CompletionRateAdapter() + + completionRateGraphView.adapter = completionRateAdapter + completionRateGraphView.baseLineColor = Color.WHITE + completionRateGraphView.scrubLineColor = Color.parseColor("#111111") + completionRateGraphView.isScrubEnabled = true + completionRateGraphView.animateChanges = true + + completionRateGraphView.setScrubListener { + val dateTimeCompletionRate = it as? DateTimeCompletionRate + + dateTimeCompletionRate?.let { + view.graph_completion_rate_title.text = it.dateTime.toString("dd MMMM, YYYY", Locale.ENGLISH) + + if (it.repositoryRoutine != null) { + val completionRate = RepositoryRoutine.getCompletionRate(it.repositoryRoutine) + + view.graph_completion_rate_value.text = "${completionRate.label}" + } else { + view.graph_completion_rate_value.text = "Not Completed" + } + } + } + + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("1W")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("1M")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("3M")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("6M")) + completionRateTabLayout.addTab(completionRateTabLayout.newTab().setText("1Y")) + + completionRateTabLayout.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + updateCompletionRateTitle() + + when (tab.position) { + 0 -> updateCompletionRateGraph(completionRateAdapter, 7) + 1 -> updateCompletionRateGraph(completionRateAdapter, 30) + 2 -> updateCompletionRateGraph(completionRateAdapter, 90) + 3 -> updateCompletionRateGraph(completionRateAdapter, 180) + else -> updateCompletionRateGraph(completionRateAdapter, 360) + } + } + + override fun onTabUnselected(tab: TabLayout.Tab) { + + } + + override fun onTabReselected(tab: TabLayout.Tab) { + + } + }) + + updateCompletionRateGraph(completionRateAdapter, 7) + updateCompletionRateTitle() + } + + fun updateCompletionRateTitle() { + val view = getView() as ProgressGeneralView + + val completionRate = RepositoryRoutine.getCompletionRate(repositoryRoutine) + + view.graph_completion_rate_title.text = DateTime(repositoryRoutine.startTime).toString("dd MMMM, YYYY", Locale.ENGLISH) + view.graph_completion_rate_value.text = "${completionRate.label}" + } + + fun updateCompletionRateGraph(adapter: CompletionRateAdapter, minusDays: Int = 7) { + val start = DateTime.now().withTimeAtStartOfDay().minusDays(minusDays) + val end = DateTime.now() + + Repository.realm.where(RepositoryRoutine::class.java) + .between("startTime", start.toDate(), end.toDate()) + .findAllAsync() + .sort("startTime", Sort.DESCENDING) + .asObservable() + .filter { it.isLoaded } + .map { + val dates = ArrayList() + + for (index in 1..minusDays) { + val date = start.plusDays(index) + + val repositoryRoutine = it.filter { + val startTime = DateTime(it.startTime) + + date.dayOfMonth == startTime.dayOfMonth + && date.monthOfYear == startTime.monthOfYear + && date.year == startTime.year + }.firstOrNull() + + if (repositoryRoutine != null) { + dates.add(DateTimeCompletionRate(date, repositoryRoutine)) + } else { + dates.add(DateTimeCompletionRate(date, null)) + } + } + + dates + } + .observeOn(AndroidSchedulers.mainThread()) + .bindToLifecycle(getView()) + .subscribe { + adapter.changeData(it) + } + } +} + +open class ProgressGeneralView : AbstractView { + override var presenter: AbstractPresenter = ProgressGeneralViewPresenter() + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + override fun onCreateView() { + super.onCreateView() + } + + fun clearCategories() { + category.removeAllViews() + } + + fun createCategory(title: String, completionRateLabel: String, completionRateValue: Float) { + val new_category = category.inflate(R.layout.view_home_category) + + new_category.title.text = title + new_category.completion_rate_label.text = completionRateLabel + new_category.completion_rate_value.setLayoutWeight(completionRateValue) + + category.addView(new_category) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/intro1.png b/app/src/main/res/drawable-xxxhdpi/intro1.png new file mode 100644 index 00000000..c18a79fb Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/intro1.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/intro2.png b/app/src/main/res/drawable-xxxhdpi/intro2.png new file mode 100644 index 00000000..47f1913e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/intro2.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/intro3.png b/app/src/main/res/drawable-xxxhdpi/intro3.png new file mode 100644 index 00000000..85643c25 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/intro3.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/intro4.png b/app/src/main/res/drawable-xxxhdpi/intro4.png new file mode 100644 index 00000000..e29c84fe Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/intro4.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/intro5.png b/app/src/main/res/drawable-xxxhdpi/intro5.png new file mode 100644 index 00000000..35fe5742 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/intro5.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/intro6.png b/app/src/main/res/drawable-xxxhdpi/intro6.png new file mode 100644 index 00000000..89f35f51 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/intro6.png differ diff --git a/app/src/main/res/drawable/action_drop_down.xml b/app/src/main/res/drawable/action_drop_down.xml new file mode 100644 index 00000000..8fc281e9 --- /dev/null +++ b/app/src/main/res/drawable/action_drop_down.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_welcome.xml b/app/src/main/res/drawable/action_welcome.xml new file mode 100644 index 00000000..8ae2a757 --- /dev/null +++ b/app/src/main/res/drawable/action_welcome.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_workout.xml b/app/src/main/res/drawable/action_workout.xml new file mode 100644 index 00000000..570b3bca --- /dev/null +++ b/app/src/main/res/drawable/action_workout.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/advanced_tuck_front_lever_row.gif b/app/src/main/res/drawable/advanced_tuck_front_lever_row.gif deleted file mode 100644 index 23b360ca..00000000 Binary files a/app/src/main/res/drawable/advanced_tuck_front_lever_row.gif and /dev/null differ diff --git a/app/src/main/res/drawable/arch.gif b/app/src/main/res/drawable/arch.gif deleted file mode 100644 index 18ec5214..00000000 Binary files a/app/src/main/res/drawable/arch.gif and /dev/null differ diff --git a/app/src/main/res/drawable/assisted_squat.gif b/app/src/main/res/drawable/assisted_squat.gif deleted file mode 100644 index 860e44e7..00000000 Binary files a/app/src/main/res/drawable/assisted_squat.gif and /dev/null differ diff --git a/app/src/main/res/drawable/band_chest_flies.gif b/app/src/main/res/drawable/band_chest_flies.gif deleted file mode 100644 index 59f342a2..00000000 Binary files a/app/src/main/res/drawable/band_chest_flies.gif and /dev/null differ diff --git a/app/src/main/res/drawable/band_dislocates.gif b/app/src/main/res/drawable/band_dislocates.gif deleted file mode 100644 index eb6510d6..00000000 Binary files a/app/src/main/res/drawable/band_dislocates.gif and /dev/null differ diff --git a/app/src/main/res/drawable/band_pull_downs.gif b/app/src/main/res/drawable/band_pull_downs.gif deleted file mode 100644 index 3c5f7e68..00000000 Binary files a/app/src/main/res/drawable/band_pull_downs.gif and /dev/null differ diff --git a/app/src/main/res/drawable/bodyweight_squat.gif b/app/src/main/res/drawable/bodyweight_squat.gif deleted file mode 100644 index c8d0a148..00000000 Binary files a/app/src/main/res/drawable/bodyweight_squat.gif and /dev/null differ diff --git a/app/src/main/res/drawable/burpee.gif b/app/src/main/res/drawable/burpee.gif deleted file mode 100644 index 8313eab1..00000000 Binary files a/app/src/main/res/drawable/burpee.gif and /dev/null differ diff --git a/app/src/main/res/drawable/cat_camel.gif b/app/src/main/res/drawable/cat_camel.gif deleted file mode 100644 index 1132c01d..00000000 Binary files a/app/src/main/res/drawable/cat_camel.gif and /dev/null differ diff --git a/app/src/main/res/drawable/dashboard_timed.xml b/app/src/main/res/drawable/dashboard_timed.xml index 0911a373..d2826074 100644 --- a/app/src/main/res/drawable/dashboard_timed.xml +++ b/app/src/main/res/drawable/dashboard_timed.xml @@ -9,8 +9,9 @@ android:pathData="M0 0h24v24H0z" /> + android:pathData="M15 1H9v2h6V1zm-4 +13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42C16.07 4.74 +14.12 4 12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9 9-4.03 +9-9c0-2.12-.74-4.07-1.97-5.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 +7-3.13 7-7 7z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/deep_stepup.gif b/app/src/main/res/drawable/deep_stepup.gif deleted file mode 100644 index b4a37db0..00000000 Binary files a/app/src/main/res/drawable/deep_stepup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/diamond_pushup.gif b/app/src/main/res/drawable/diamond_pushup.gif deleted file mode 100644 index 31d3d28e..00000000 Binary files a/app/src/main/res/drawable/diamond_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/floor_l_sit.gif b/app/src/main/res/drawable/floor_l_sit.gif deleted file mode 100644 index b022b1b7..00000000 Binary files a/app/src/main/res/drawable/floor_l_sit.gif and /dev/null differ diff --git a/app/src/main/res/drawable/freestanding_handstand.gif b/app/src/main/res/drawable/freestanding_handstand.gif deleted file mode 100644 index 9ce5f3ac..00000000 Binary files a/app/src/main/res/drawable/freestanding_handstand.gif and /dev/null differ diff --git a/app/src/main/res/drawable/hollow_hold.gif b/app/src/main/res/drawable/hollow_hold.gif deleted file mode 100644 index 29a51fde..00000000 Binary files a/app/src/main/res/drawable/hollow_hold.gif and /dev/null differ diff --git a/app/src/main/res/drawable/horizontal_row.gif b/app/src/main/res/drawable/horizontal_row.gif deleted file mode 100644 index b9594c86..00000000 Binary files a/app/src/main/res/drawable/horizontal_row.gif and /dev/null differ diff --git a/app/src/main/res/drawable/incline_pushup.gif b/app/src/main/res/drawable/incline_pushup.gif deleted file mode 100644 index 33e87a2a..00000000 Binary files a/app/src/main/res/drawable/incline_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/incline_row.gif b/app/src/main/res/drawable/incline_row.gif deleted file mode 100644 index a2b4c28e..00000000 Binary files a/app/src/main/res/drawable/incline_row.gif and /dev/null differ diff --git a/app/src/main/res/drawable/l_ring_dips.gif b/app/src/main/res/drawable/l_ring_dips.gif deleted file mode 100644 index 50ba5f1f..00000000 Binary files a/app/src/main/res/drawable/l_ring_dips.gif and /dev/null differ diff --git a/app/src/main/res/drawable/l_sit.gif b/app/src/main/res/drawable/l_sit.gif deleted file mode 100644 index 9197e339..00000000 Binary files a/app/src/main/res/drawable/l_sit.gif and /dev/null differ diff --git a/app/src/main/res/drawable/leg_swings.gif b/app/src/main/res/drawable/leg_swings.gif deleted file mode 100644 index cc244b77..00000000 Binary files a/app/src/main/res/drawable/leg_swings.gif and /dev/null differ diff --git a/app/src/main/res/drawable/lsit_pullup.gif b/app/src/main/res/drawable/lsit_pullup.gif deleted file mode 100644 index b2ef7ec9..00000000 Binary files a/app/src/main/res/drawable/lsit_pullup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/negative_pullup.gif b/app/src/main/res/drawable/negative_pullup.gif deleted file mode 100644 index ebf76123..00000000 Binary files a/app/src/main/res/drawable/negative_pullup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/one_foot_l_sit.gif b/app/src/main/res/drawable/one_foot_l_sit.gif deleted file mode 100644 index 8e34cc79..00000000 Binary files a/app/src/main/res/drawable/one_foot_l_sit.gif and /dev/null differ diff --git a/app/src/main/res/drawable/parallel_bar_dips.gif b/app/src/main/res/drawable/parallel_bar_dips.gif deleted file mode 100644 index 40323082..00000000 Binary files a/app/src/main/res/drawable/parallel_bar_dips.gif and /dev/null differ diff --git a/app/src/main/res/drawable/parallel_bar_hold.gif b/app/src/main/res/drawable/parallel_bar_hold.gif deleted file mode 100644 index 61f70c39..00000000 Binary files a/app/src/main/res/drawable/parallel_bar_hold.gif and /dev/null differ diff --git a/app/src/main/res/drawable/plank.gif b/app/src/main/res/drawable/plank.gif deleted file mode 100644 index 88b5bd28..00000000 Binary files a/app/src/main/res/drawable/plank.gif and /dev/null differ diff --git a/app/src/main/res/drawable/pseudo_planche.gif b/app/src/main/res/drawable/pseudo_planche.gif deleted file mode 100644 index 50aacb30..00000000 Binary files a/app/src/main/res/drawable/pseudo_planche.gif and /dev/null differ diff --git a/app/src/main/res/drawable/pseudo_planche_pushup.gif b/app/src/main/res/drawable/pseudo_planche_pushup.gif deleted file mode 100644 index e14ee56f..00000000 Binary files a/app/src/main/res/drawable/pseudo_planche_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/pullover.gif b/app/src/main/res/drawable/pullover.gif deleted file mode 100644 index dfafebb7..00000000 Binary files a/app/src/main/res/drawable/pullover.gif and /dev/null differ diff --git a/app/src/main/res/drawable/pullup.gif b/app/src/main/res/drawable/pullup.gif deleted file mode 100644 index 6dc92692..00000000 Binary files a/app/src/main/res/drawable/pullup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/pushup.gif b/app/src/main/res/drawable/pushup.gif deleted file mode 100644 index 3295dc66..00000000 Binary files a/app/src/main/res/drawable/pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/reverse_plank.gif b/app/src/main/res/drawable/reverse_plank.gif deleted file mode 100644 index c81c93ba..00000000 Binary files a/app/src/main/res/drawable/reverse_plank.gif and /dev/null differ diff --git a/app/src/main/res/drawable/ring_dips.gif b/app/src/main/res/drawable/ring_dips.gif deleted file mode 100644 index 64ce60ee..00000000 Binary files a/app/src/main/res/drawable/ring_dips.gif and /dev/null differ diff --git a/app/src/main/res/drawable/ring_pushup.gif b/app/src/main/res/drawable/ring_pushup.gif deleted file mode 100644 index 6c5dfa46..00000000 Binary files a/app/src/main/res/drawable/ring_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/ring_support.gif b/app/src/main/res/drawable/ring_support.gif deleted file mode 100644 index 96ce21c0..00000000 Binary files a/app/src/main/res/drawable/ring_support.gif and /dev/null differ diff --git a/app/src/main/res/drawable/ring_wide_pushup.gif b/app/src/main/res/drawable/ring_wide_pushup.gif deleted file mode 100644 index 6c5dfa46..00000000 Binary files a/app/src/main/res/drawable/ring_wide_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/rings_turned_out_pushup.gif b/app/src/main/res/drawable/rings_turned_out_pushup.gif deleted file mode 100644 index 96a2e2e6..00000000 Binary files a/app/src/main/res/drawable/rings_turned_out_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/rings_turned_out_support.gif b/app/src/main/res/drawable/rings_turned_out_support.gif deleted file mode 100644 index d10259fd..00000000 Binary files a/app/src/main/res/drawable/rings_turned_out_support.gif and /dev/null differ diff --git a/app/src/main/res/drawable/scapular_shrugs.gif b/app/src/main/res/drawable/scapular_shrugs.gif deleted file mode 100644 index 3952f9f7..00000000 Binary files a/app/src/main/res/drawable/scapular_shrugs.gif and /dev/null differ diff --git a/app/src/main/res/drawable/shoulder_rolls.gif b/app/src/main/res/drawable/shoulder_rolls.gif deleted file mode 100644 index f75f6c32..00000000 Binary files a/app/src/main/res/drawable/shoulder_rolls.gif and /dev/null differ diff --git a/app/src/main/res/drawable/side_plank.gif b/app/src/main/res/drawable/side_plank.gif deleted file mode 100644 index 1fee2e56..00000000 Binary files a/app/src/main/res/drawable/side_plank.gif and /dev/null differ diff --git a/app/src/main/res/drawable/squat_jumps.gif b/app/src/main/res/drawable/squat_jumps.gif deleted file mode 100644 index 979429ae..00000000 Binary files a/app/src/main/res/drawable/squat_jumps.gif and /dev/null differ diff --git a/app/src/main/res/drawable/tuck_front_lever.gif b/app/src/main/res/drawable/tuck_front_lever.gif deleted file mode 100644 index 44f9637d..00000000 Binary files a/app/src/main/res/drawable/tuck_front_lever.gif and /dev/null differ diff --git a/app/src/main/res/drawable/tuck_front_lever_row.gif b/app/src/main/res/drawable/tuck_front_lever_row.gif deleted file mode 100644 index 497c570b..00000000 Binary files a/app/src/main/res/drawable/tuck_front_lever_row.gif and /dev/null differ diff --git a/app/src/main/res/drawable/tuck_ice_cream_maker.gif b/app/src/main/res/drawable/tuck_ice_cream_maker.gif deleted file mode 100644 index 2c1ff486..00000000 Binary files a/app/src/main/res/drawable/tuck_ice_cream_maker.gif and /dev/null differ diff --git a/app/src/main/res/drawable/tuck_l_sit.gif b/app/src/main/res/drawable/tuck_l_sit.gif deleted file mode 100644 index b36cea08..00000000 Binary files a/app/src/main/res/drawable/tuck_l_sit.gif and /dev/null differ diff --git a/app/src/main/res/drawable/vertical_row.gif b/app/src/main/res/drawable/vertical_row.gif deleted file mode 100644 index 3d11aafd..00000000 Binary files a/app/src/main/res/drawable/vertical_row.gif and /dev/null differ diff --git a/app/src/main/res/drawable/wall_handstand.gif b/app/src/main/res/drawable/wall_handstand.gif deleted file mode 100644 index 9c4c24f7..00000000 Binary files a/app/src/main/res/drawable/wall_handstand.gif and /dev/null differ diff --git a/app/src/main/res/drawable/wall_plank.gif b/app/src/main/res/drawable/wall_plank.gif deleted file mode 100644 index f4c283f7..00000000 Binary files a/app/src/main/res/drawable/wall_plank.gif and /dev/null differ diff --git a/app/src/main/res/drawable/wall_pushup.gif b/app/src/main/res/drawable/wall_pushup.gif deleted file mode 100644 index 7167aac6..00000000 Binary files a/app/src/main/res/drawable/wall_pushup.gif and /dev/null differ diff --git a/app/src/main/res/drawable/wide_row.gif b/app/src/main/res/drawable/wide_row.gif deleted file mode 100644 index 147e81e3..00000000 Binary files a/app/src/main/res/drawable/wide_row.gif and /dev/null differ diff --git a/app/src/main/res/drawable/wrist_progression.gif b/app/src/main/res/drawable/wrist_progression.gif deleted file mode 100644 index 029db6fa..00000000 Binary files a/app/src/main/res/drawable/wrist_progression.gif and /dev/null differ diff --git a/app/src/main/res/layout-port/view_timer.xml b/app/src/main/res/layout-port/view_timer.xml index 2054530b..8183c94d 100644 --- a/app/src/main/res/layout-port/view_timer.xml +++ b/app/src/main/res/layout-port/view_timer.xml @@ -2,7 +2,7 @@ + android:layout_below="@+id/view_toolbar" /> + + + android:layout_below="@+id/view_toolbar"/> @@ -39,5 +43,5 @@ android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/activity_main_header" - app:menu="@menu/drawer" /> + app:menu="@menu/menu_drawer" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main_header.xml b/app/src/main/res/layout/activity_main_header.xml index 83d84232..5b64e297 100644 --- a/app/src/main/res/layout/activity_main_header.xml +++ b/app/src/main/res/layout/activity_main_header.xml @@ -2,6 +2,7 @@ diff --git a/app/src/main/res/layout/activity_progress.xml b/app/src/main/res/layout/activity_progress.xml index 09771fd6..4c1c0735 100644 --- a/app/src/main/res/layout/activity_progress.xml +++ b/app/src/main/res/layout/activity_progress.xml @@ -4,7 +4,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#EEEEEE"> + android:background="#F5F5F7"> diff --git a/app/src/main/res/layout/activity_progress_card.xml b/app/src/main/res/layout/activity_progress_card.xml index 742377cd..7dd4a1a0 100644 --- a/app/src/main/res/layout/activity_progress_card.xml +++ b/app/src/main/res/layout/activity_progress_card.xml @@ -1,5 +1,7 @@ - + android:layout_margin="8dp"> - + android:layout_height="wrap_content" + android:paddingTop="16dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingBottom="8dp" + android:orientation="vertical"> + + + + + + + + android:paddingLeft="8dp" + android:paddingBottom="8dp">