From 65d8a6fe101234a4c387da5683916511338ff24a Mon Sep 17 00:00:00 2001 From: Christopher Beckmann Date: Thu, 7 Sep 2017 02:04:21 +0200 Subject: [PATCH] Exercises can now be added and deleted to and from Sets. Spinner now saves the last pressed entry. --- app/src/main/AndroidManifest.xml | 13 +- .../activities/AboutActivity.java | 2 +- .../activities/ChooseExerciseActivity.java | 122 ++++++++++ .../activities/EditExerciseSetActivity.java | 149 ++++++++++-- .../ManageExerciseSetsActivity.java | 2 +- .../activities/TimerActivity.java | 158 +++++++----- .../activities/adapter/ExerciseAdapter.java | 230 ++++++++++++++++++ .../adapter/ExerciseSetAdapter.java | 8 +- .../adapter/ExerciseSetSpinnerAdapter.java | 2 + .../activities/layout/FlowLayout.java | 166 +++++++++++++ .../database/SQLiteHelper.java | 84 +++++-- .../database/data/ExerciseSet.java | 3 + .../{ => exercises}/ExerciseLocale.java | 6 +- .../exercises/ExerciseSections.java | 36 +++ .../service/TimerService.java | 74 ++++-- app/src/main/res/drawable/circular.xml | 22 -- .../main/res/drawable/ic_edit_white_24dp.xml | 9 + .../res/layout/activity_choose_exercise.xml | 35 +++ .../res/layout/activity_edit_exercise_set.xml | 75 +++--- app/src/main/res/layout/activity_timer.xml | 13 +- .../res/layout/layout_exercise_grid_item.xml | 51 ++++ .../main/res/layout/layout_exercise_set.xml | 1 + app/src/main/res/values/attrs.xml | 11 + app/src/main/res/values/strings.xml | 9 + 24 files changed, 1082 insertions(+), 199 deletions(-) create mode 100644 app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ChooseExerciseActivity.java create mode 100644 app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseAdapter.java create mode 100644 app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/layout/FlowLayout.java rename app/src/main/java/org/secuso/privacyfriendlybreakreminder/{ => exercises}/ExerciseLocale.java (84%) create mode 100644 app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseSections.java delete mode 100644 app/src/main/res/drawable/circular.xml create mode 100644 app/src/main/res/drawable/ic_edit_white_24dp.xml create mode 100644 app/src/main/res/layout/activity_choose_exercise.xml create mode 100644 app/src/main/res/layout/layout_exercise_grid_item.xml create mode 100644 app/src/main/res/values/attrs.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5cf9a2d..db8df51 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,10 +69,10 @@ android:theme="@style/SplashTheme"> + - @@ -86,7 +86,6 @@ android:name=".activities.TimerActivity" android:label="@string/activity_title_break_reminder" android:theme="@style/AppTheme.NoActionBar" /> - - - + + > + + \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/AboutActivity.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/AboutActivity.java index d50d10e..099f31b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/AboutActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/AboutActivity.java @@ -35,9 +35,9 @@ public class AboutActivity extends AppCompatActivity { private void setupActionBar() { ActionBar actionBar = getSupportActionBar(); - actionBar.setTitle(R.string.about); if (actionBar != null) { // Show the Up button in the action bar. + actionBar.setTitle(R.string.about); actionBar.setDisplayHomeAsUpEnabled(true); } } diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ChooseExerciseActivity.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ChooseExerciseActivity.java new file mode 100644 index 0000000..f2a19d5 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ChooseExerciseActivity.java @@ -0,0 +1,122 @@ +package org.secuso.privacyfriendlybreakreminder.activities; + +import android.content.Intent; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.util.SortedList; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageView; + +import org.secuso.privacyfriendlybreakreminder.R; +import org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseAdapter; +import org.secuso.privacyfriendlybreakreminder.activities.layout.FlowLayout; +import org.secuso.privacyfriendlybreakreminder.database.SQLiteHelper; +import org.secuso.privacyfriendlybreakreminder.database.data.Exercise; +import org.secuso.privacyfriendlybreakreminder.exercises.ExerciseLocale; +import org.secuso.privacyfriendlybreakreminder.exercises.ExerciseSections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import static org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseAdapter.ID_COMPARATOR; + +public class ChooseExerciseActivity extends AppCompatActivity { + + private static final String TAG = ChooseExerciseActivity.class.getSimpleName(); + + public static final String EXTRA_SELECTED_EXERCISES = TAG+".EXTRA_SELECTED_EXERCISES"; + + FlowLayout filterButtonLayout; + RecyclerView exerciseList; + + ExerciseAdapter exerciseAdapter; + SQLiteHelper databaseHelper; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_choose_exercise); + + initResources(); + + ActionBar ab = getSupportActionBar(); + if(ab != null) { + ab.setDisplayHomeAsUpEnabled(true); + } + + int[] chosenExercises = getIntent().getIntArrayExtra(EXTRA_SELECTED_EXERCISES); + List chosenExercisesList = new ArrayList<>(); + + for(int i : chosenExercises) chosenExercisesList.add(i); + + exerciseAdapter.add(databaseHelper.getExerciseList(ExerciseLocale.getLocale())); + exerciseAdapter.setCheckedItems(chosenExercisesList); + } + + private void initResources() { + databaseHelper = new SQLiteHelper(this); + + filterButtonLayout = (FlowLayout) findViewById(R.id.layout_filter_buttons); + exerciseList = (RecyclerView) findViewById(R.id.exercise_list); + exerciseAdapter = new ExerciseAdapter(this, ID_COMPARATOR); + exerciseAdapter.showCheckboxes(true); + + GridLayoutManager gridLayout = new GridLayoutManager(this, 3); + exerciseList.setLayoutManager(gridLayout); + exerciseList.setAdapter(exerciseAdapter); + + filterButtonLayout.removeAllViews(); + + for(ExerciseSections section : ExerciseSections.getSectionList()) { + // TODO: Add Buttons for every section we have + //View view = LayoutInflater.from(this).inflate(R.layout.layout_section_filter_button, null, false); + //TextView image = (TextView) view.findViewById(R.id.button_text); + //filterButtonLayout.addView(view); + } + } + + public void onClick(View v) { + + // TODO get onclicklistener to call this method so we can filter the list + //exerciseAdapter.replaceAll(databaseHelper.getExerciseListBySections()); + exerciseList.scrollToPosition(0); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + sendDataBack(); + super.onBackPressed(); + } + + private void sendDataBack() { + Intent result = new Intent(); + + List selectedIdList = exerciseAdapter.getCheckedIds(); + int[] selectedIds = new int[selectedIdList.size()]; + + for(int i = 0; i < selectedIds.length; ++i) { + selectedIds[i] = selectedIdList.get(i); + } + + result.putExtra(EXTRA_SELECTED_EXERCISES, selectedIds); + setResult(RESULT_OK, result); + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/EditExerciseSetActivity.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/EditExerciseSetActivity.java index d959f26..41ed9e5 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/EditExerciseSetActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/EditExerciseSetActivity.java @@ -23,32 +23,43 @@ import android.view.MenuItem; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; -import org.secuso.privacyfriendlybreakreminder.ExerciseLocale; +import org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseAdapter; +import org.secuso.privacyfriendlybreakreminder.database.data.Exercise; +import org.secuso.privacyfriendlybreakreminder.exercises.ExerciseLocale; import org.secuso.privacyfriendlybreakreminder.R; -import org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseSetAdapter; import org.secuso.privacyfriendlybreakreminder.database.SQLiteHelper; import org.secuso.privacyfriendlybreakreminder.database.data.ExerciseSet; +import java.util.ArrayList; +import java.util.List; + +import static org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseAdapter.ID_COMPARATOR; + public class EditExerciseSetActivity extends AppCompatActivity implements android.support.v4.app.LoaderManager.LoaderCallbacks { // extras public static final String EXTRA_EXERCISE_SET_ID = "EXTRA_EXERCISE_SET_ID"; public static final String EXTRA_EXERCISE_SET_NAME = "EXTRA_EXERCISE_SET_NAME"; + private static final int PICK_EXERCISE_REQUEST = 1; // The request code + // UI private TextView exerciseSetNameText; private RecyclerView exerciseList; private ProgressBar loadingSpinner; - private ExerciseSetAdapter exerciseSetAdapter; + private ExerciseAdapter mAdapter; private ActionBar actionBar; private Toolbar toolbar; // exercise set information private long exerciseSetId = -1L; private String exerciseSetName = ""; + private boolean nameChanged = false; private boolean modificationsDone = false; + private SQLiteHelper mDbHelper; //methods @@ -72,13 +83,13 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi } private void initResources() { + mDbHelper = new SQLiteHelper(this); toolbar = (Toolbar) findViewById(R.id.toolbar); exerciseSetNameText = (TextView) findViewById(R.id.exercise_set_name); exerciseList = (RecyclerView) findViewById(R.id.exercise_list); - exerciseSetAdapter = new ExerciseSetAdapter(this, null); - exerciseList.setAdapter(exerciseSetAdapter); - //exerciseList.setLayoutManager(new GridLayoutManager(this, 2)); - exerciseList.setLayoutManager(new LinearLayoutManager(this)); + mAdapter = new ExerciseAdapter(this, ID_COMPARATOR); + exerciseList.setAdapter(mAdapter); + exerciseList.setLayoutManager(new GridLayoutManager(this, 3)); loadingSpinner = (ProgressBar) findViewById(R.id.loading_spinner); exerciseSetNameText.setText(exerciseSetName); @@ -91,7 +102,7 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi @Override public void afterTextChanged(Editable editable) { - modificationsDone = true; + nameChanged = true; } }); @@ -117,9 +128,7 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi return new AsyncTaskLoader(this) { @Override public ExerciseSet loadInBackground() { - SQLiteHelper helper = new SQLiteHelper(getContext()); - - return helper.getExerciseListForSet((int)exerciseSetId, ExerciseLocale.getLocale()); + return mDbHelper.getExerciseListForSet((int)exerciseSetId, ExerciseLocale.getLocale()); } @Override @@ -142,7 +151,12 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi } }); - exerciseSetAdapter.updateData(set); + if(set != null) { + mAdapter.replaceAll(set.getExercises()); + } + + // load data only once + getSupportLoaderManager().destroyLoader(0); } @Override @@ -159,22 +173,40 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - if(modificationsDone) { + if(modificationsDone()) { showDiscardDialog(); } else { super.onBackPressed(); } return true; case R.id.save: - saveChanges(); - super.onBackPressed(); + if(TextUtils.getTrimmedLength(exerciseSetNameText.getText()) == 0) { + Toast.makeText(this, R.string.activity_edit_no_empty_name, Toast.LENGTH_SHORT).show(); + } else { + saveChanges(); + super.onBackPressed(); + } return true; } return super.onOptionsItemSelected(item); } private void saveChanges() { - ExerciseSet set = exerciseSetAdapter.getExerciseSet(); + List set = mAdapter.getExercises(); + + if(modificationsDone) { + mDbHelper.clearExercisesFromSet((int) exerciseSetId); + + for (Exercise e : set) { + mDbHelper.addExerciseToExerciseSet((int) exerciseSetId, e.getId()); + } + } + if(nameChanged) { + ExerciseSet exerciseSet = new ExerciseSet(); + exerciseSet.setId(exerciseSetId); + exerciseSet.setName(exerciseSetNameText.getText().toString()); + mDbHelper.updateExerciseSet(exerciseSet); + } // TODO: save changes to database // man könnte den unterschied, der gespeichert werden muss rausfinden, indem man nur die änderungen speichert.. @@ -191,13 +223,17 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi @Override public void onBackPressed() { - if(modificationsDone) { + if(modificationsDone()) { showDiscardDialog(); } else { super.onBackPressed(); } } + private boolean modificationsDone() { + return nameChanged || modificationsDone; + } + private void showDiscardDialog() { new AlertDialog.Builder(this) .setPositiveButton(R.string.keep_editing, new DialogInterface.OnClickListener() { @@ -215,4 +251,83 @@ public class EditExerciseSetActivity extends AppCompatActivity implements androi .setMessage(R.string.dialog_discard_confirmation) .create().show(); } + + public void onClick(View view) { + switch(view.getId()) { + case R.id.add_button: + + Intent i = new Intent(this, ChooseExerciseActivity.class); + i.putExtra(ChooseExerciseActivity.EXTRA_SELECTED_EXERCISES , getSelectedExerciseIds()); + startActivityForResult(i, PICK_EXERCISE_REQUEST); + break; + } + } + + private int[] getSelectedExerciseIds() { + List set = mAdapter.getExercises(); + + int[] result = new int[set.size()]; + + for(int i = 0; i < set.size(); ++i) { + result[i] = set.get(i).getId(); + } + return result; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if(requestCode == PICK_EXERCISE_REQUEST) { + if(resultCode == RESULT_OK) { + + int[] result = data.getIntArrayExtra(ChooseExerciseActivity.EXTRA_SELECTED_EXERCISES); + List oldList = mAdapter.getExercises(); + // did we make any changes? + + boolean needToUpdate = false; + + if(result.length != oldList.size()) { + modificationsDone = true; + needToUpdate = true; + } + + if(!needToUpdate) { + for (int id : result) { + + boolean found = false; + + for (Exercise e : oldList) { + if (e.getId() == id) { + found = true; + break; + } + } + + if (!found) { + modificationsDone = true; + needToUpdate = true; + break; + } + } + } + + if(needToUpdate) { + List allExercises = mDbHelper.getExerciseList(ExerciseLocale.getLocale()); + List newList = new ArrayList<>(); + + for (int id : result) { + for (Exercise e : allExercises) { + if (e.getId() == id) { + newList.add(e); + break; + } + } + } + + mAdapter.replaceAll(newList); + } + } + } + } } diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ManageExerciseSetsActivity.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ManageExerciseSetsActivity.java index de04c28..5e02a4a 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ManageExerciseSetsActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/ManageExerciseSetsActivity.java @@ -32,7 +32,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import org.secuso.privacyfriendlybreakreminder.ExerciseLocale; +import org.secuso.privacyfriendlybreakreminder.exercises.ExerciseLocale; import org.secuso.privacyfriendlybreakreminder.R; import org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseSetListAdapter; import org.secuso.privacyfriendlybreakreminder.activities.helper.BaseActivity; diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/TimerActivity.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/TimerActivity.java index b75f07d..de34ddf 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/TimerActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/TimerActivity.java @@ -14,15 +14,13 @@ import android.graphics.drawable.ColorDrawable; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.annotation.ColorRes; -import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.ContextCompat; import android.support.v4.content.Loader; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; -import android.widget.ArrayAdapter; +import android.widget.AdapterView; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.NumberPicker; @@ -30,7 +28,7 @@ import android.widget.ProgressBar; import android.widget.Spinner; import android.widget.TextView; -import org.secuso.privacyfriendlybreakreminder.ExerciseLocale; +import org.secuso.privacyfriendlybreakreminder.exercises.ExerciseLocale; import org.secuso.privacyfriendlybreakreminder.R; import org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseSetSpinnerAdapter; import org.secuso.privacyfriendlybreakreminder.activities.helper.BaseActivity; @@ -62,6 +60,7 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap // animation private int mShortAnimationDuration; + private boolean currentStatusIsPickerVisible = false; private static final String[] SECONDS_MINUTES = new String[60]; private static final String[] HOURS = new String[24]; @@ -105,11 +104,9 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap boolean isRunning = intent.getBooleanExtra("isRunning", false); boolean isPaused = intent.getBooleanExtra("isPaused", false); - if(mTimerService != null) { - updateUI(); - } else { - updateUI(isRunning, isPaused, initialDuration, millisUntilDone); - } + Log.d(TAG, millisUntilDone + "/" + initialDuration + " (" + (isRunning ? "Running" : "") + (isPaused ? "Paused" : "") + (!isRunning && !isPaused ? "Stopped" : "") + ")"); + + updateUI(isRunning, isPaused, initialDuration, millisUntilDone); } }; @@ -175,7 +172,7 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap } private void initResources() { - SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); exerciseSetAdapter = new ExerciseSetSpinnerAdapter(this, R.layout.layout_exercise_set, new LinkedList()); @@ -186,6 +183,14 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap resetButton = (ImageButton) findViewById(R.id.button_reset); exerciseSetSpinner = (Spinner) findViewById(R.id.spinner_choose_exercise_set); exerciseSetSpinner.setAdapter(exerciseSetAdapter); + exerciseSetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + pref.edit().putLong("DEFAULT_EXERCISE_SET", id).apply(); + } + @Override + public void onNothingSelected(AdapterView parent) {} + }); secondsPicker = (NumberPicker) findViewById(R.id.seconds_picker); minutesPicker = (NumberPicker) findViewById(R.id.minutes_picker); hoursPicker = (NumberPicker) findViewById(R.id.hours_picker); @@ -240,14 +245,13 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap handlePlayPressed(); break; case R.id.button_reset: - mTimerService.stopAndResetTimer(); + if(mTimerService != null) + mTimerService.stopAndResetTimer(); break; //case R.id.button_chooseExercise: // startActivity(new Intent(this, ManageExerciseSetsActivity.class)); // break; } - - updateUI(); } private void handlePlayPressed() { @@ -262,7 +266,7 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap long duration = getCurrentSetDuration(); saveCurrentSetDuration(); mTimerService.startTimer(duration); - progressBar.setMax((int)duration); + progressBar.setMax((int) duration); } } } @@ -289,68 +293,80 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap } } - private void updateUI(boolean running, boolean isPaused, long initialDuration, long remainingDuration) { + private synchronized void updateUI(boolean running, boolean isPaused, long initialDuration, long remainingDuration) { updatePlayButton(running); progressBar.setMax((int) initialDuration); updateProgress(remainingDuration); showPicker(!running && !isPaused); } - private void showPicker(boolean showPicker) { - if(showPicker && pickerLayout.getVisibility() != View.VISIBLE) { - pickerLayout.setAlpha(0f); - pickerLayout.setVisibility(View.VISIBLE); - pickerLayout.animate() - .alpha(1f) - .setStartDelay(mShortAnimationDuration) - .setDuration(mShortAnimationDuration) - .setListener(null); + private synchronized void showPicker(final boolean showPicker) { + if(showPicker != currentStatusIsPickerVisible) { - timerText.animate() - .alpha(0f) - .setDuration(mShortAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - timerText.setVisibility(View.INVISIBLE); - } - }); + pickerLayout.clearAnimation(); + timerText.clearAnimation(); + progressBar.clearAnimation(); - progressBar.animate() - .alpha(0f) - .setDuration(mShortAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - progressBar.setVisibility(View.INVISIBLE); - } - }); + currentStatusIsPickerVisible = showPicker; - } else if(!showPicker && pickerLayout.getVisibility() == View.VISIBLE) { - pickerLayout.animate() - .alpha(0f) - .setDuration(mShortAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - pickerLayout.setVisibility(View.INVISIBLE); - } - }); + if (showPicker) { + pickerLayout.setAlpha(0f); + pickerLayout.setVisibility(View.VISIBLE); + pickerLayout.animate() + .alpha(1f) + //.setStartDelay(mShortAnimationDuration) + .setDuration(mShortAnimationDuration) + .setListener(null); - timerText.setAlpha(0f); - timerText.setVisibility(View.VISIBLE); - timerText.animate() - .alpha(1f) - .setDuration(mShortAnimationDuration) - .setListener(null); + timerText.animate() + .alpha(0f) + .setDuration(mShortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if(currentStatusIsPickerVisible) + timerText.setVisibility(View.INVISIBLE); + } + }); - progressBar.setAlpha(0f); - progressBar.setVisibility(View.VISIBLE); - progressBar.animate() - .alpha(1f) - .setDuration(mShortAnimationDuration) - .setStartDelay(mShortAnimationDuration) - .setListener(null); + progressBar.animate() + .alpha(0f) + .setDuration(mShortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if(currentStatusIsPickerVisible) + progressBar.setVisibility(View.INVISIBLE); + } + }); + + } else { + pickerLayout.animate() + .alpha(0f) + .setDuration(mShortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if(!currentStatusIsPickerVisible) + pickerLayout.setVisibility(View.INVISIBLE); + } + }); + + timerText.setAlpha(0f); + timerText.setVisibility(View.VISIBLE); + timerText.animate() + .alpha(1f) + .setDuration(mShortAnimationDuration) + .setListener(null); + + progressBar.setAlpha(0f); + progressBar.setVisibility(View.VISIBLE); + progressBar.animate() + .alpha(1f) + .setDuration(mShortAnimationDuration) + //.setStartDelay(mShortAnimationDuration) + .setListener(null); + } } } @@ -400,6 +416,18 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap @Override public void onLoadFinished(Loader> loader, List data) { exerciseSetAdapter.updateData(data); + + long defaultId = PreferenceManager.getDefaultSharedPreferences(this).getLong("DEFAULT_EXERCISE_SET", 0L); + + + for(int i = 0; i < data.size(); ++i) { + ExerciseSet e = data.get(i); + + if(e.getId() == defaultId) { + exerciseSetSpinner.setSelection(i); + break; + } + } } @Override diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseAdapter.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseAdapter.java new file mode 100644 index 0000000..f308167 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseAdapter.java @@ -0,0 +1,230 @@ +package org.secuso.privacyfriendlybreakreminder.activities.adapter; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.constraint.ConstraintLayout; +import android.support.v7.util.SortedList; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import org.secuso.privacyfriendlybreakreminder.R; +import org.secuso.privacyfriendlybreakreminder.activities.ChooseExerciseActivity; +import org.secuso.privacyfriendlybreakreminder.database.data.Exercise; +import org.secuso.privacyfriendlybreakreminder.database.data.ExerciseSet; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Christopher Beckmann on 06.09.2017. + */ + +public class ExerciseAdapter extends RecyclerView.Adapter { + + private Context mContext; + private List checkedIds = new ArrayList<>(); + private boolean mShowCheckboxes = false; + + public static final Comparator ID_COMPARATOR = new Comparator() { + @Override + public int compare(Exercise a, Exercise b) { + return (a.getId() < b.getId()) ? -1 : ((a.getId() == b.getId()) ? 0 : 1); + } + }; + + private final LayoutInflater mInflater; + private final Comparator mComparator; + private final SortedList mSortedList = new SortedList<>(Exercise.class, new SortedList.Callback() { + @Override + public void onInserted(int position, int count) { + notifyItemRangeInserted(position, count); + } + + @Override + public void onRemoved(int position, int count) { + notifyItemRangeRemoved(position, count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + notifyItemMoved(fromPosition, toPosition); + } + + @Override + public void onChanged(int position, int count) { + notifyItemRangeChanged(position, count); + } + + @Override + public int compare(Exercise a, Exercise b) { + return mComparator.compare(a, b); + } + + @Override + public boolean areContentsTheSame(Exercise oldItem, Exercise newItem) { + return oldItem.equals(newItem); + } + + @Override + public boolean areItemsTheSame(Exercise item1, Exercise item2) { + return item1.getId() == item2.getId(); + } + }); + + public ExerciseAdapter(Context context, Comparator comparator) { + this.mContext = context; + this.mComparator = comparator; + this.mInflater = LayoutInflater.from(context); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View itemView = mInflater.inflate(R.layout.layout_exercise_grid_item, parent, false); + return new ExerciseAdapter.ExerciseViewHolder(itemView); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + + final Exercise exercise = mSortedList.get(position); + + final ExerciseViewHolder vh = (ExerciseViewHolder) holder; + + String imageID = exercise.getImageID(); + String[] imageIDSplit = imageID.split(","); + + if(imageIDSplit.length > 1) { + imageID = imageIDSplit[0]; + } + + final View.OnClickListener infoClick = new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(mContext, "Show Details Of the Exercise Now.", Toast.LENGTH_SHORT).show(); + // TODO: show Fragement ? Dialog? Something with the Information of the Exercise! + } + }; + + final View.OnClickListener checkClick = new View.OnClickListener() { + @Override + public void onClick(View v) { + if(vh.checkbox.isChecked()) { + checkedIds.remove(checkedIds.indexOf(exercise.getId())); + } else { + checkedIds.add(exercise.getId()); + } + + vh.checkbox.setChecked(!vh.checkbox.isChecked()); + } + }; + + int imageResID = mContext.getResources().getIdentifier( + "exercise_" + imageID, + "drawable", + mContext.getPackageName()); + vh.image.setImageResource(imageResID); + + if(checkedIds != null) + vh.checkbox.setChecked(checkedIds.contains(exercise.getId())); + + vh.checkbox.setClickable(false); + vh.checkbox.setVisibility(mShowCheckboxes ? View.VISIBLE : View.GONE); + + vh.layout.setOnClickListener(mShowCheckboxes ? checkClick : infoClick); + vh.layout.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + infoClick.onClick(v); + return true; + } + }); + + vh.infoButton.setOnClickListener(infoClick); + } + + @Override + public int getItemCount() { + return mSortedList.size(); + } + + public void replaceAll(@NonNull List exercises) { + mSortedList.beginBatchedUpdates(); + for (int i = mSortedList.size() - 1; i >= 0; i--) { + final Exercise ex = mSortedList.get(i); + if (!exercises.contains(ex)) { + mSortedList.remove(ex); + } + } + mSortedList.addAll(exercises); + mSortedList.endBatchedUpdates(); + } + + public List getCheckedIds() { + return checkedIds; + } + + public void add(Exercise model) { + mSortedList.add(model); + } + + public void remove(Exercise model) { + mSortedList.remove(model); + } + + public void add(List models) { + mSortedList.addAll(models); + } + + public void remove(List models) { + mSortedList.beginBatchedUpdates(); + for (Exercise model : models) { + mSortedList.remove(model); + } + mSortedList.endBatchedUpdates(); + } + + public void setCheckedItems(@NonNull List checkedItems) { + this.checkedIds = checkedItems; + } + + public List getExercises() { + List result = new LinkedList<>(); + + for (int i = 0; i < mSortedList.size(); ++i) { + Exercise e = mSortedList.get(i); + result.add(e); + } + + return result; + } + + public class ExerciseViewHolder extends RecyclerView.ViewHolder { + + ImageView image; + CheckBox checkbox; + ImageButton infoButton; + ConstraintLayout layout; + + public ExerciseViewHolder(View itemView) { + super(itemView); + + layout = (ConstraintLayout) itemView.findViewById(R.id.exercise_layout); + checkbox = (CheckBox) itemView.findViewById(R.id.exercise_checkbox); + image = (ImageView) itemView.findViewById(R.id.exercise_image); + infoButton = (ImageButton) itemView.findViewById(R.id.exercise_info_button); + } + } + + public void showCheckboxes(boolean show) { + mShowCheckboxes = show; + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetAdapter.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetAdapter.java index 4519337..250dd0a 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetAdapter.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetAdapter.java @@ -11,14 +11,14 @@ import android.widget.ImageView; import android.widget.TextView; import org.secuso.privacyfriendlybreakreminder.R; -import org.secuso.privacyfriendlybreakreminder.activities.ManageExerciseSetsActivity; -import org.secuso.privacyfriendlybreakreminder.activities.helper.CursorRecyclerViewAdapter; -import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseColumns; import org.secuso.privacyfriendlybreakreminder.database.data.Exercise; import org.secuso.privacyfriendlybreakreminder.database.data.ExerciseSet; /** - * Created by Christopher Beckmann on 30.08.2017. + * This adapter provides the {@link Exercise}s of one {@link ExerciseSet}. + * Use {@link ExerciseSetAdapter#add(Exercise)} and {@link ExerciseSetAdapter#remove(Exercise)} to manage the {@link Exercise}s. + * @author Christopher Beckmann + * @see android.support.v7.widget.RecyclerView.Adapter */ public class ExerciseSetAdapter extends RecyclerView.Adapter { diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetSpinnerAdapter.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetSpinnerAdapter.java index f9b10e4..93a46e6 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetSpinnerAdapter.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/adapter/ExerciseSetSpinnerAdapter.java @@ -3,6 +3,7 @@ package org.secuso.privacyfriendlybreakreminder.activities.adapter; import android.content.Context; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.CardView; import android.view.LayoutInflater; import android.view.View; @@ -93,6 +94,7 @@ public class ExerciseSetSpinnerAdapter extends ArrayAdapter { } else { noExercisesText.setVisibility(View.GONE); } + noExercisesText.setTextColor(ContextCompat.getColor(getContext(), R.color.black)); //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE); //View row = inflater.inflate(resource, parent, false); diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/layout/FlowLayout.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/layout/FlowLayout.java new file mode 100644 index 0000000..c44a89d --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/activities/layout/FlowLayout.java @@ -0,0 +1,166 @@ +package org.secuso.privacyfriendlybreakreminder.activities.layout; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import org.secuso.privacyfriendlybreakreminder.R; + +public class FlowLayout extends ViewGroup { + private int mHorizontalSpacing; + private int mVerticalSpacing; + private Paint mPaint; + + public FlowLayout(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout); + try { + mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0); + mVerticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0); + } finally { + a.recycle(); + } + + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setColor(0xffff0000); + mPaint.setStrokeWidth(2.0f); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight(); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + + boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED; + + int width = 0; + int height = getPaddingTop(); + + int currentWidth = getPaddingLeft(); + int currentHeight = 0; + + boolean breakLine = false; + boolean newLine = false; + int spacing = 0; + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + measureChild(child, widthMeasureSpec, heightMeasureSpec); + + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + spacing = mHorizontalSpacing; + if (lp.horizontalSpacing >= 0) { + spacing = lp.horizontalSpacing; + } + + if (growHeight && (breakLine || currentWidth + child.getMeasuredWidth() > widthSize)) { + height += currentHeight + mVerticalSpacing; + currentHeight = 0; + width = Math.max(width, currentWidth - spacing); + currentWidth = getPaddingLeft(); + newLine = true; + } else { + newLine = false; + } + + lp.x = currentWidth; + lp.y = height; + + currentWidth += child.getMeasuredWidth() + spacing; + currentHeight = Math.max(currentHeight, child.getMeasuredHeight()); + + breakLine = lp.breakLine; + } + + if (!newLine) { + height += currentHeight; + width = Math.max(width, currentWidth - spacing); + } + + width += getPaddingRight(); + height += getPaddingBottom(); + + setMeasuredDimension(resolveSize(width, widthMeasureSpec), + resolveSize(height, heightMeasureSpec)); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight()); + } + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean more = super.drawChild(canvas, child, drawingTime); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.horizontalSpacing > 0) { + float x = child.getRight(); + float y = child.getTop() + child.getHeight() / 2.0f; + canvas.drawLine(x, y - 4.0f, x, y + 4.0f, mPaint); + canvas.drawLine(x, y, x + lp.horizontalSpacing, y, mPaint); + canvas.drawLine(x + lp.horizontalSpacing, y - 4.0f, x + lp.horizontalSpacing, y + 4.0f, mPaint); + } + if (lp.breakLine) { + float x = child.getRight(); + float y = child.getTop() + child.getHeight() / 2.0f; + canvas.drawLine(x, y, x, y + 6.0f, mPaint); + canvas.drawLine(x, y + 6.0f, x + 6.0f, y + 6.0f, mPaint); + } + return more; + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p.width, p.height); + } + + public static class LayoutParams extends ViewGroup.LayoutParams { + int x; + int y; + + public int horizontalSpacing; + public boolean breakLine; + + public LayoutParams(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout_LayoutParams); + try { + horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, -1); + breakLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_breakLine, false); + } finally { + a.recycle(); + } + } + + public LayoutParams(int w, int h) { + super(w, h); + } + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/SQLiteHelper.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/SQLiteHelper.java index fec20cb..e34493e 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/SQLiteHelper.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/SQLiteHelper.java @@ -6,6 +6,7 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.support.annotation.NonNull; import android.util.Log; import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseSetColumns; @@ -72,13 +73,29 @@ public class SQLiteHelper extends SQLiteOpenHelper { database.close(); } + public void updateExerciseSet(ExerciseSet exerciseSet) { + SQLiteDatabase database = getReadableDatabase(); + database.update(ExerciseSetColumns.TABLE_NAME, ExerciseSetColumns.getValues(exerciseSet), ExerciseSetColumns._ID + " = ?", new String[]{String.valueOf(exerciseSet.getId())}); + database.close(); + } + public synchronized long addExerciseSet(String name) { SQLiteDatabase database = getReadableDatabase(); ContentValues cv = new ContentValues(); cv.put(ExerciseSetColumns.NAME, name); - return database.insert(ExerciseSetColumns.TABLE_NAME, null, cv); + long id = database.insert(ExerciseSetColumns.TABLE_NAME, null, cv); + database.close(); + + return id; + } + + + public void clearExercisesFromSet(int exerciseSetId) { + SQLiteDatabase database = getReadableDatabase(); + database.delete("exercise_set_exercises", ExerciseSetColumns._ID + " = ?", new String[]{String.valueOf(exerciseSetId)}); + database.close(); } public synchronized void addExerciseToExerciseSet(int exerciseSetId, int exerciseId) { @@ -131,14 +148,17 @@ public class SQLiteHelper extends SQLiteOpenHelper { } c.moveToNext(); } + c.close(); } + close(); + return result; } public synchronized Cursor getExerciseCursor(String language) { SQLiteDatabase database = getReadableDatabase(); - return database.rawQuery(buildQuery(false), new String[]{language}); + return database.rawQuery(buildQuery(0), new String[]{language}); } public synchronized List getExerciseList(String language) { @@ -196,6 +216,8 @@ public class SQLiteHelper extends SQLiteOpenHelper { c.close(); } + close(); + return result; } @@ -214,41 +236,45 @@ public class SQLiteHelper extends SQLiteOpenHelper { c.close(); } + close(); + return result; } - - private String buildQuery(boolean addSectionCheck) { - return buildQuery(addSectionCheck, ""); - } - - public synchronized List getExercisesFromSection(String language, String section) { // TODO: Rename after old activities are deleted + public synchronized List getExerciseListBySections(String language, @NonNull List sections) { SQLiteDatabase database = getReadableDatabase(); - Cursor c = database.rawQuery(buildQuery(true), new String[]{language, "%"+section+"%"}); + String[] argValues = new String[sections.size() + 1]; + + argValues[0] = language; + + for(int i = 1; i < argValues.length; ++i) { + argValues[i] = "%"+sections.get(i - 1)+"%"; + } + + Cursor c = database.rawQuery(buildQuery(sections.size()), argValues); + + return buildExerciseList(c); + } + + public synchronized List getExercisesFromSection(String language, String section) { // TODO: REMOVE after old activities are deleted + SQLiteDatabase database = getReadableDatabase(); + + Cursor c = database.rawQuery(buildQuery(1), new String[]{language, "%"+section+"%"}); return buildExerciseList(c); } /** - * SELECT - * E._id, - * E.section, - * E.image_id, - * L.local_id, - * L.language, - * L.exercise_id, - * L.name, - * L.description, - * L.execution + * SELECT * * FROM exercises E LEFT OUTER JOIN exercises_local L * ON E._id = L.exercise_id - * WHERE L.language = "de" [AND E.section LIKE %?%] + * WHERE L.language = "de" [AND E.section LIKE ?] * ORDER BY E._id ASC * * @return the sql query without the ; at the end. */ - private String buildQuery(boolean addSectionCheck, String customWhereClause) { + private String buildQuery(int sectionCheck) { StringBuilder sqlQuery = new StringBuilder(); sqlQuery.append("SELECT "); @@ -279,12 +305,23 @@ public class SQLiteHelper extends SQLiteOpenHelper { sqlQuery.append(ExerciseLocalColumns.LANGUAGE); sqlQuery.append(" = ? "); - if(addSectionCheck) { - sqlQuery.append("AND E."); + if(sectionCheck > 0) { + sqlQuery.append("AND ( "); + } + + for(int i = 0; i < sectionCheck; ++i) { + sqlQuery.append("E."); sqlQuery.append(ExerciseColumns.SECTION); sqlQuery.append(" LIKE ? "); + + if(i + 1 == sectionCheck) { + sqlQuery.append(") "); + } else { + sqlQuery.append("OR "); + } } + sqlQuery.append("ORDER BY E."); sqlQuery.append(ExerciseColumns._ID); sqlQuery.append(" ASC"); @@ -364,5 +401,4 @@ public class SQLiteHelper extends SQLiteOpenHelper { } } } - } diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/data/ExerciseSet.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/data/ExerciseSet.java index 8766078..f09a920 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/data/ExerciseSet.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/database/data/ExerciseSet.java @@ -56,4 +56,7 @@ public class ExerciseSet { return exercises.size(); } + public List getExercises() { + return exercises; + } } diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/ExerciseLocale.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseLocale.java similarity index 84% rename from app/src/main/java/org/secuso/privacyfriendlybreakreminder/ExerciseLocale.java rename to app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseLocale.java index 0fdba78..77eafb1 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/ExerciseLocale.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseLocale.java @@ -1,4 +1,4 @@ -package org.secuso.privacyfriendlybreakreminder; +package org.secuso.privacyfriendlybreakreminder.exercises; import java.util.Arrays; import java.util.HashSet; @@ -8,7 +8,9 @@ import java.util.Locale; * This class saves the available languages for the exercises. * @author Christopher Beckmann */ -public class ExerciseLocale { +public final class ExerciseLocale { + + private ExerciseLocale() {} private static final HashSet AVAILABLE_LOCALE = new HashSet<>(); diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseSections.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseSections.java new file mode 100644 index 0000000..c8a58c4 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/exercises/ExerciseSections.java @@ -0,0 +1,36 @@ +package org.secuso.privacyfriendlybreakreminder.exercises; + +import android.content.Context; +import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; + +import org.secuso.privacyfriendlybreakreminder.R; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Christopher Beckmann on 06.09.2017. + */ + +public enum ExerciseSections { + Head(R.string.exercise_section_head), + Neck(R.string.exercise_section_neck), + Arms(R.string.exercise_section_arms), + Torso(R.string.exercise_section_torso), + Spinal(R.string.exercise_section_spinal), + Pelvis(R.string.exercise_section_pelvis), + Legs(R.string.exercise_section_legs); + + private final @StringRes int nameResId; + + ExerciseSections(@StringRes int resId) { + this.nameResId = resId; + } + + public String getLocalName(Context context) { + return context.getString(nameResId); + } + public static List getSectionList() { return Arrays.asList( Head, Neck, Arms, Torso, Spinal, Pelvis, Legs ); } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/service/TimerService.java b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/service/TimerService.java index a7920fb..96123a8 100644 --- a/app/src/main/java/org/secuso/privacyfriendlybreakreminder/service/TimerService.java +++ b/app/src/main/java/org/secuso/privacyfriendlybreakreminder/service/TimerService.java @@ -28,12 +28,17 @@ import java.util.Timer; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; public class TimerService extends Service { + public static final String TAG = TimerService.class.getSimpleName(); public static final String NOTIFICATION_BROADCAST = TAG + ".NOTIFICATION_BROADCAST"; public static final String TIMER_BROADCAST = TAG + ".TIMER_BROADCAST"; + private static final int UPDATE_INTERVAL = 25; + private static final int NOTIFICATION_ID = 31337; + private TimerServiceBinder mBinder = new TimerServiceBinder(); private CountDownTimer mTimer; + private NotificationManager notificationManager; private boolean isRunning = false; private long remainingDuration = 0; @@ -43,14 +48,17 @@ public class TimerService extends Service { int lastTime = 0; @Override public void onReceive(Context context, Intent intent) { - if((int) remainingDuration / 1000 != lastTime) { - lastTime = (int) remainingDuration / 1000; - updateNotification(); - } - if(intent.getBooleanExtra("done" ,false)) { + // limit the notification updates + int remainingSeconds = intent.getIntExtra("countdown_seconds", 0); + + if(remainingSeconds != lastTime) { + lastTime = remainingSeconds; updateNotification(); + } else if(intent.getBooleanExtra("done" ,false)) { + lastTime = 0; + updateNotification(); } } }; @@ -72,10 +80,19 @@ public class TimerService extends Service { public synchronized void startTimer(long duration) { if(!isRunning) { initialDuration = duration; - mTimer = createTimer(duration); - mTimer.start(); - isRunning = true; - sendBroadcast(buildBroadcast()); + + if(duration > 0) { + mTimer = createTimer(duration); + mTimer.start(); + isRunning = true; + sendBroadcast(buildBroadcast()); + + } else { + remainingDuration = initialDuration; + Intent broadcast = buildBroadcast(); + broadcast.putExtra("done", true); + sendBroadcast(broadcast); + } } } @@ -83,6 +100,7 @@ public class TimerService extends Service { if(isRunning) { mTimer.cancel(); isRunning = false; + sendBroadcast(buildBroadcast()); } } @@ -92,6 +110,7 @@ public class TimerService extends Service { mTimer = createTimer(remainingDuration); mTimer.start(); isRunning = true; + sendBroadcast(buildBroadcast()); } } @@ -103,6 +122,7 @@ public class TimerService extends Service { mTimer.start(); } remainingDuration = initialDuration; + sendBroadcast(buildBroadcast()); } @@ -110,10 +130,11 @@ public class TimerService extends Service { if(isRunning) mTimer.cancel(); isRunning = false; remainingDuration = initialDuration; + sendBroadcast(buildBroadcast()); } - public synchronized boolean isPaused() { return !isRunning && remainingDuration > 0 && remainingDuration != initialDuration; } + public synchronized boolean isPaused() { return !isRunning && initialDuration != 0 && remainingDuration > 0 && remainingDuration != initialDuration; } public synchronized boolean isRunning() { return isRunning; @@ -122,24 +143,29 @@ public class TimerService extends Service { private CountDownTimer createTimer(long duration) { remainingDuration = duration; - return new CountDownTimer(duration, 10) { + return new CountDownTimer(duration, UPDATE_INTERVAL) { @Override public void onTick(long millisUntilFinished) { - synchronized (TimerService.this) { + if(isRunning) { remainingDuration = millisUntilFinished; + sendBroadcast(buildBroadcast()); } - - sendBroadcast(buildBroadcast()); } @Override public void onFinish() { + + mTimer.cancel(); + isRunning = false; + remainingDuration = 0; + Intent broadcast = buildBroadcast(); broadcast.putExtra("done", true); - sendBroadcast(buildBroadcast()); + sendBroadcast(broadcast); + + remainingDuration = initialDuration; - stopAndResetTimer(); } }; } @@ -153,6 +179,7 @@ public class TimerService extends Service { broadcast.putExtra("countdown_seconds", secondsUntilFinished); broadcast.putExtra("isRunning", isRunning()); broadcast.putExtra("isPaused", isPaused()); + return (broadcast); } @@ -160,15 +187,11 @@ public class TimerService extends Service { public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); - setAsForegroundService(); + notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); return START_STICKY; } - private void setAsForegroundService() { - startForeground(31337, buildNotification()); - } - private Notification buildNotification() { NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentTitle(getString(R.string.app_name)); @@ -187,13 +210,18 @@ public class TimerService extends Service { builder.setPriority(NotificationCompat.PRIORITY_HIGH); builder.setWhen(0); builder.setSmallIcon(R.mipmap.ic_launcher); + builder.setOngoing(isRunning() || isPaused()); return builder.build(); } private void updateNotification() { - NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - notificationManager.notify(31337, buildNotification()); + if(isRunning() || isPaused()) + startForeground(NOTIFICATION_ID, buildNotification()); + else + stopForeground(false); + + notificationManager.notify(NOTIFICATION_ID, buildNotification()); } @Override diff --git a/app/src/main/res/drawable/circular.xml b/app/src/main/res/drawable/circular.xml deleted file mode 100644 index d448196..0000000 --- a/app/src/main/res/drawable/circular.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_edit_white_24dp.xml b/app/src/main/res/drawable/ic_edit_white_24dp.xml new file mode 100644 index 0000000..35a774a --- /dev/null +++ b/app/src/main/res/drawable/ic_edit_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_choose_exercise.xml b/app/src/main/res/layout/activity_choose_exercise.xml new file mode 100644 index 0000000..d19930c --- /dev/null +++ b/app/src/main/res/layout/activity_choose_exercise.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_edit_exercise_set.xml b/app/src/main/res/layout/activity_edit_exercise_set.xml index 447584b..f231ecb 100644 --- a/app/src/main/res/layout/activity_edit_exercise_set.xml +++ b/app/src/main/res/layout/activity_edit_exercise_set.xml @@ -44,39 +44,52 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="org.secuso.privacyfriendlybreakreminder.activities.ManageExerciseSetsActivity"> - + - + - + + + diff --git a/app/src/main/res/layout/activity_timer.xml b/app/src/main/res/layout/activity_timer.xml index faa97f3..98d0e7f 100644 --- a/app/src/main/res/layout/activity_timer.xml +++ b/app/src/main/res/layout/activity_timer.xml @@ -48,7 +48,7 @@ android:layout_marginTop="8dp" app:layout_constraintHorizontal_bias="0.0" android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" /> + android:layout_marginEnd="8dp"/>