Added a screen for the break and also introduced ExerciseSets. Database restructured and added methods to the SQLDatabaseHelper.
This commit is contained in:
parent
955ecf1d8f
commit
0d7b3a52fc
29 changed files with 1830 additions and 284 deletions
|
|
@ -11,7 +11,6 @@
|
|||
android:screenOrientation="portrait"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity
|
||||
android:name=".activities.BreakReminder"
|
||||
android:label="@string/app_name"
|
||||
|
|
@ -19,6 +18,8 @@
|
|||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
|
@ -65,12 +66,11 @@
|
|||
<activity
|
||||
android:name=".activities.TimerActivity"
|
||||
android:launchMode="singleInstance">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<service
|
||||
|
|
@ -78,6 +78,8 @@
|
|||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name=".activities.ExerciseSetOverviewActivity" />
|
||||
<activity android:name=".activities.ExerciseActivity"></activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Binary file not shown.
|
|
@ -12,7 +12,7 @@ import android.widget.Button;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.database.DBHandler;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.SQLiteHelper;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.*;
|
||||
import org.secuso.privacyfriendlybreakreminder.R;
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ public class BreakActivity extends AppCompatActivity implements View.OnClickList
|
|||
private int currentExercise, breakTime = 0, currentExerciseSection;
|
||||
private ImageView image;
|
||||
private String[] exercises;
|
||||
private DBHandler dbHandler;
|
||||
private SQLiteHelper SQLiteHelper;
|
||||
private List<List<Exercise>> allAvailableExercises;
|
||||
private List<Integer> sections;
|
||||
private Random random;
|
||||
|
|
@ -86,7 +86,7 @@ public class BreakActivity extends AppCompatActivity implements View.OnClickList
|
|||
ct_text.setText(bufferZeroMinute + mins + ":00");
|
||||
ct_text.setOnClickListener(this);
|
||||
|
||||
dbHandler = new DBHandler(this);
|
||||
SQLiteHelper = new SQLiteHelper(this);
|
||||
random = new Random();
|
||||
sections = new ArrayList<>();
|
||||
setRandomExercises();
|
||||
|
|
@ -253,7 +253,7 @@ public class BreakActivity extends AppCompatActivity implements View.OnClickList
|
|||
while (notFoundYet) {
|
||||
currentExerciseSection = random.nextInt(exercises.length);
|
||||
if (!usedSectionsString.contains(exercises[currentExerciseSection])) {
|
||||
List<Exercise> list = dbHandler.getExercisesFromSection("de",exercises[currentExerciseSection]);
|
||||
List<Exercise> list = SQLiteHelper.getExercisesFromSection("de",exercises[currentExerciseSection]);
|
||||
allAvailableExercises.add(list);
|
||||
usedSectionsString += exercises[currentExerciseSection] + ".";
|
||||
editor.putString("currently_done_exercises", usedSectionsString);
|
||||
|
|
@ -452,7 +452,7 @@ public class BreakActivity extends AppCompatActivity implements View.OnClickList
|
|||
|
||||
//Close database connection
|
||||
if (!noExercises)
|
||||
dbHandler.close();
|
||||
SQLiteHelper.close();
|
||||
finish();
|
||||
}
|
||||
}.start();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.activities;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.R;
|
||||
|
||||
public class ExerciseActivity extends AppCompatActivity {
|
||||
|
||||
private ImageView playButton;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_exercise);
|
||||
|
||||
initResources();
|
||||
}
|
||||
|
||||
private void initResources() {
|
||||
playButton = (ImageView) findViewById(R.id.button_playPause);
|
||||
}
|
||||
|
||||
private void updatePlayButton(boolean isRunning) {
|
||||
if(isRunning) {
|
||||
playButton.setImageResource(R.drawable.ic_pause_black_48dp);
|
||||
} else {
|
||||
playButton.setImageResource(R.drawable.ic_play_arrow_black_48dp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.activities;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.R;
|
||||
import org.secuso.privacyfriendlybreakreminder.activities.adapter.ExerciseAdapter;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.SQLiteHelper;
|
||||
|
||||
public class ExerciseSetOverviewActivity extends AppCompatActivity implements android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor>{
|
||||
|
||||
private TextView exerciseSetName;
|
||||
private RecyclerView exerciseList;
|
||||
|
||||
private ExerciseAdapter exerciseAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_exercise_set);
|
||||
|
||||
initResources();
|
||||
}
|
||||
|
||||
private void initResources() {
|
||||
exerciseSetName = (TextView) findViewById(R.id.exercise_set_name);
|
||||
exerciseList = (RecyclerView) findViewById(R.id.exercise_list);
|
||||
exerciseAdapter = new ExerciseAdapter(this, null);
|
||||
exerciseList.setAdapter(exerciseAdapter);
|
||||
exerciseList.setLayoutManager(new GridLayoutManager(this, 2));
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, final Bundle args) {
|
||||
return new AsyncTaskLoader<Cursor>(this) {
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
SQLiteHelper helper = new SQLiteHelper(getContext());
|
||||
return helper.getExerciseCursorForSet(2, "de"); // TODO; get correct subset list
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
forceLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
cursor.moveToFirst();
|
||||
exerciseAdapter.changeCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
((ExerciseAdapter) exerciseList.getAdapter()).changeCursor(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
getSupportLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.activities;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
|
|
@ -7,23 +9,61 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.NumberPicker;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.R;
|
||||
import org.secuso.privacyfriendlybreakreminder.service.TimerService;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class TimerActivity extends AppCompatActivity {
|
||||
private static final String TAG = TimerActivity.class.getSimpleName();
|
||||
private static final String PREF_PICKER_SECONDS = TAG + ".PREF_PICKER_SECONDS";
|
||||
private static final String PREF_PICKER_MINUTES = TAG + ".PREF_PICKER_MINUTES";
|
||||
private static final String PREF_PICKER_HOURS = TAG + ".PREF_PICKER_HOURS";
|
||||
|
||||
// UI
|
||||
private ProgressBar progressBar;
|
||||
private TextView timerText;
|
||||
private ImageButton playButton;
|
||||
private ImageButton resetButton;
|
||||
private NumberPicker secondsPicker;
|
||||
private NumberPicker minutesPicker;
|
||||
private NumberPicker hoursPicker;
|
||||
private LinearLayout pickerLayout;
|
||||
|
||||
// animation
|
||||
private int mShortAnimationDuration;
|
||||
|
||||
private static final String[] SECONDS_MINUTES = new String[60];
|
||||
private static final String[] HOURS = new String[24];
|
||||
|
||||
static {
|
||||
for(int i = 0; i < SECONDS_MINUTES.length; ++i) {
|
||||
SECONDS_MINUTES[i] = String.format(Locale.US, "%02d", i);
|
||||
}
|
||||
for(int i = 0; i < HOURS.length; ++i) {
|
||||
HOURS[i] = String.format(Locale.US, "%02d", i);
|
||||
}
|
||||
}
|
||||
|
||||
// Service
|
||||
private TimerService mTimerService = null;
|
||||
|
|
@ -44,17 +84,22 @@ public class TimerActivity extends AppCompatActivity {
|
|||
};
|
||||
|
||||
private void onServiceConnected() {
|
||||
if(mTimerService.isRunning()) {
|
||||
progressBar.setMax((int) mTimerService.getInitialDuration());
|
||||
}
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private final BroadcastReceiver timerReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
long millisUntilDone = intent.getLongExtra("onTickMillis", -1L);
|
||||
long initialDuration = intent.getLongExtra("initialMillis", 0L);
|
||||
boolean isRunning = intent.getBooleanExtra("isRunning", false);
|
||||
boolean isPaused = intent.getBooleanExtra("isPaused", false);
|
||||
|
||||
updateProgress(millisUntilDone);
|
||||
if(mTimerService != null) {
|
||||
updateUI();
|
||||
} else {
|
||||
updateUI(isRunning, isPaused, initialDuration, millisUntilDone);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,6 +135,7 @@ public class TimerActivity extends AppCompatActivity {
|
|||
if(mTimerService != null && !mTimerService.isRunning()) {
|
||||
updateProgress(mTimerService.getInitialDuration());
|
||||
}
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -111,19 +157,39 @@ public class TimerActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
private void initResources() {
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
|
||||
|
||||
progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
timerText = (TextView) findViewById(R.id.timerText);
|
||||
playButton = (ImageButton) findViewById(R.id.button_playPause);
|
||||
resetButton = (ImageButton) findViewById(R.id.button_reset);
|
||||
|
||||
timerText.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int duration = 1000 * 60 * 1; // 1 minutes
|
||||
if(mTimerService != null) {
|
||||
mTimerService.startTimer(duration);
|
||||
progressBar.setMax(duration);
|
||||
}
|
||||
}
|
||||
});
|
||||
secondsPicker = (NumberPicker) findViewById(R.id.seconds_picker);
|
||||
minutesPicker = (NumberPicker) findViewById(R.id.minutes_picker);
|
||||
hoursPicker = (NumberPicker) findViewById(R.id.hours_picker);
|
||||
|
||||
secondsPicker.setDisplayedValues(SECONDS_MINUTES);
|
||||
secondsPicker.setMinValue(0);
|
||||
secondsPicker.setMaxValue(SECONDS_MINUTES.length - 1);
|
||||
secondsPicker.setValue(pref.getInt(PREF_PICKER_SECONDS, 0));
|
||||
|
||||
minutesPicker.setDisplayedValues(SECONDS_MINUTES);
|
||||
minutesPicker.setMinValue(0);
|
||||
minutesPicker.setMaxValue(SECONDS_MINUTES.length - 1);
|
||||
minutesPicker.setValue(pref.getInt(PREF_PICKER_MINUTES, 30));
|
||||
|
||||
hoursPicker.setDisplayedValues(HOURS);
|
||||
hoursPicker.setMinValue(0);
|
||||
hoursPicker.setMaxValue(HOURS.length - 1);
|
||||
hoursPicker.setValue(pref.getInt(PREF_PICKER_HOURS, 1));
|
||||
|
||||
setDividerColor(secondsPicker, R.color.transparent);
|
||||
setDividerColor(minutesPicker, R.color.transparent);
|
||||
setDividerColor(hoursPicker, R.color.transparent);
|
||||
|
||||
pickerLayout = (LinearLayout) findViewById(R.id.picker_layout);
|
||||
}
|
||||
|
||||
private void updateProgress(long millisUntilFinished) {
|
||||
|
|
@ -135,15 +201,8 @@ public class TimerActivity extends AppCompatActivity {
|
|||
int seconds = secondsUntilFinished % 60;
|
||||
int minutes = minutesUntilFinished % 60;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(hours > 0) sb.append(hours).append(":");
|
||||
if(minutes < 10) sb.append(0);
|
||||
sb.append(minutes).append(":");
|
||||
if(seconds < 10) sb.append(0);
|
||||
sb.append(seconds);
|
||||
|
||||
timerText.setText(sb.toString());
|
||||
String time = String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);
|
||||
timerText.setText(time);
|
||||
|
||||
//progressBar.setMax(1000);
|
||||
//ObjectAnimator animation = ObjectAnimator.ofInt(progressBar, "progress", 0, 1000 * percentFinished); // see this max value coming back here, we animale towards that value
|
||||
|
|
@ -154,8 +213,148 @@ public class TimerActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
|
||||
public void onClick(View view) {
|
||||
switch(view.getId()) {
|
||||
case R.id.button_playPause:
|
||||
case R.id.progressBar:
|
||||
handlePlayPressed();
|
||||
break;
|
||||
case R.id.button_reset:
|
||||
mTimerService.stopAndResetTimer();
|
||||
break;
|
||||
case R.id.button_chooseExercise:
|
||||
startActivity(new Intent(this, ExerciseSetOverviewActivity.class));
|
||||
break;
|
||||
}
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void handlePlayPressed() {
|
||||
if(mTimerService != null) {
|
||||
if(mTimerService.isPaused()) {
|
||||
mTimerService.resumeTimer();
|
||||
}
|
||||
else if(mTimerService.isRunning()){
|
||||
mTimerService.pauseTimer();
|
||||
|
||||
} else {
|
||||
long duration = getCurrentSetDuration();
|
||||
saveCurrentSetDuration();
|
||||
mTimerService.startTimer(duration);
|
||||
progressBar.setMax((int)duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCurrentSetDuration() {
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
pref.edit().putInt(PREF_PICKER_SECONDS, secondsPicker.getValue())
|
||||
.putInt(PREF_PICKER_MINUTES, minutesPicker.getValue())
|
||||
.putInt(PREF_PICKER_HOURS, hoursPicker.getValue()).apply();
|
||||
}
|
||||
|
||||
private long getCurrentSetDuration() {
|
||||
long duration = secondsPicker.getValue() * 1000;
|
||||
duration += minutesPicker.getValue() * 1000 * 60;
|
||||
duration += hoursPicker.getValue() * 1000 * 60 * 60;
|
||||
return duration;
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if(mTimerService != null) {
|
||||
updateUI(mTimerService.isRunning(), mTimerService.isPaused(), mTimerService.getInitialDuration(), mTimerService.getRemainingDuration());
|
||||
} else {
|
||||
showPicker(true);
|
||||
}
|
||||
}
|
||||
|
||||
private 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);
|
||||
|
||||
timerText.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(mShortAnimationDuration)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
timerText.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
progressBar.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(mShortAnimationDuration)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
} 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);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlayButton(boolean isRunning) {
|
||||
if(isRunning) {
|
||||
playButton.setImageResource(R.drawable.ic_pause_black_48dp);
|
||||
} else {
|
||||
playButton.setImageResource(R.drawable.ic_play_arrow_black_48dp);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDividerColor(NumberPicker picker, @ColorRes int color) {
|
||||
java.lang.reflect.Field[] pickerFields = NumberPicker.class.getDeclaredFields();
|
||||
for (java.lang.reflect.Field pf : pickerFields) {
|
||||
if (pf.getName().equals("mSelectionDivider")) {
|
||||
pf.setAccessible(true);
|
||||
try {
|
||||
ColorDrawable colorDrawable = new ColorDrawable(ContextCompat.getColor(this, color));
|
||||
pf.set(picker, colorDrawable);
|
||||
} catch (IllegalArgumentException | Resources.NotFoundException | IllegalAccessException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.activities.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.R;
|
||||
import org.secuso.privacyfriendlybreakreminder.activities.helper.CursorRecyclerViewAdapter;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseColumns;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
||||
|
||||
/**
|
||||
* Created by Christopher Beckmann on 30.08.2017.
|
||||
*/
|
||||
public class ExerciseAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
|
||||
|
||||
|
||||
public ExerciseAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor, ExerciseColumns._ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_exercise, parent, false);
|
||||
return new ExerciseViewHolder(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
|
||||
final Exercise exercise = ExerciseColumns.fromCursor(cursor);
|
||||
|
||||
ExerciseViewHolder vh = (ExerciseViewHolder) viewHolder;
|
||||
|
||||
String imageID = exercise.getImageID();
|
||||
String[] imageIDSplit = imageID.split(",");
|
||||
|
||||
if(imageIDSplit.length > 1) {
|
||||
imageID = imageIDSplit[0]; // only take the first image as a display image
|
||||
}
|
||||
|
||||
int imageResID = mContext.getResources().getIdentifier(
|
||||
"exercise_" + imageID,
|
||||
"drawable",
|
||||
mContext.getPackageName());
|
||||
vh.image.setImageResource(imageResID);
|
||||
vh.name.setText(exercise.getName());
|
||||
vh.executionText.setText(exercise.getExecution());
|
||||
vh.descriptionText.setText(exercise.getDescription());
|
||||
vh.section.setText(exercise.getSection());
|
||||
}
|
||||
|
||||
public class ExerciseViewHolder extends ViewHolder {
|
||||
|
||||
ImageView image;
|
||||
TextView name;
|
||||
TextView executionText;
|
||||
TextView descriptionText;
|
||||
TextView section;
|
||||
|
||||
public ExerciseViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
name = (TextView) itemView.findViewById(R.id.exercise_name);
|
||||
image = (ImageView) itemView.findViewById(R.id.exercise_image);
|
||||
executionText = (TextView) itemView.findViewById(R.id.exercise_execution);
|
||||
descriptionText = (TextView) itemView.findViewById(R.id.exercise_description);
|
||||
section = (TextView) itemView.findViewById(R.id.exercise_section);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.activities.helper;
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Matthieu Harlé
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.widget.Filter;
|
||||
|
||||
/**
|
||||
* Taken from: https://gist.github.com/Shywim/127f207e7248fe48400b
|
||||
*/
|
||||
public class CursorFilter extends Filter {
|
||||
|
||||
CursorFilterClient mClient;
|
||||
|
||||
interface CursorFilterClient {
|
||||
CharSequence convertToString(Cursor cursor);
|
||||
Cursor runQueryOnBackgroundThread(CharSequence constraint);
|
||||
Cursor getCursor();
|
||||
void changeCursor(Cursor cursor);
|
||||
}
|
||||
|
||||
CursorFilter(CursorFilterClient client) {
|
||||
mClient = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence convertResultToString(Object resultValue) {
|
||||
return mClient.convertToString((Cursor) resultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
Cursor cursor = mClient.runQueryOnBackgroundThread(constraint);
|
||||
|
||||
FilterResults results = new FilterResults();
|
||||
if (cursor != null) {
|
||||
results.count = cursor.getCount();
|
||||
results.values = cursor;
|
||||
} else {
|
||||
results.count = 0;
|
||||
results.values = null;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
Cursor oldCursor = mClient.getCursor();
|
||||
|
||||
if (results.values != null && results.values != oldCursor) {
|
||||
mClient.changeCursor((Cursor) results.values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.activities.helper;
|
||||
/*
|
||||
* Copyright (C) 2014 skyfish.jy@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Matthieu Harlé
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorJoiner;
|
||||
import android.database.DataSetObserver;
|
||||
import android.os.Handler;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Filter;
|
||||
import android.widget.FilterQueryProvider;
|
||||
import android.widget.Filterable;
|
||||
|
||||
/**
|
||||
* Provide a {@link android.support.v7.widget.RecyclerView.Adapter} implementation with cursor
|
||||
* support.
|
||||
*
|
||||
* Child classes only need to implement {@link #onCreateViewHolder(android.view.ViewGroup, int)} and
|
||||
* {@link #onBindViewHolder(RecyclerView.ViewHolder, Cursor)}.
|
||||
*
|
||||
* This class does not implement deprecated fields and methods from CursorAdapter! Incidentally,
|
||||
* only {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER} is available, so the
|
||||
* flag is implied, and only the Adapter behavior using this flag has been ported.
|
||||
*
|
||||
* @param <VH> {@inheritDoc}
|
||||
*
|
||||
* @see android.support.v7.widget.RecyclerView.Adapter
|
||||
* @see android.widget.CursorAdapter
|
||||
* @see Filterable
|
||||
* @see CursorFilter.CursorFilterClient
|
||||
*/
|
||||
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements CursorFilter.CursorFilterClient, Filterable {
|
||||
|
||||
protected Context mContext;
|
||||
private Cursor mCursor;
|
||||
private boolean mDataValid;
|
||||
private int mRowIdColumn;
|
||||
private DataSetObserver mDataSetObserver;
|
||||
private ChangeObserver mChangeObserver;
|
||||
private CursorFilter mCursorFilter;
|
||||
private FilterQueryProvider mFilterQueryProvider;
|
||||
private boolean mUseIndividualNotifies;
|
||||
|
||||
private String mIdIdentifier = "_id";
|
||||
|
||||
public CursorRecyclerViewAdapter(Context context, Cursor cursor, String idIdentifier) {
|
||||
this(context, null);
|
||||
this.setIdIdentifier(idIdentifier);
|
||||
this.changeCursor(cursor);
|
||||
}
|
||||
|
||||
public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
|
||||
mContext = context;
|
||||
mCursor = cursor;
|
||||
mDataValid = cursor != null;
|
||||
mRowIdColumn = mDataValid ? mCursor.getColumnIndex(mIdIdentifier) : -1;
|
||||
|
||||
mDataSetObserver = new NotifyingDataSetObserver();
|
||||
mChangeObserver = new ChangeObserver();
|
||||
|
||||
if (mDataValid) {
|
||||
if (mChangeObserver != null) cursor.registerContentObserver(mChangeObserver);
|
||||
if (mDataSetObserver != null) cursor.registerDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIdIdentifier(String identifier) {
|
||||
mIdIdentifier = TextUtils.isEmpty(identifier) ?
|
||||
"_id" :
|
||||
identifier;
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (mDataValid && mCursor != null) {
|
||||
return mCursor.getCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
|
||||
return mCursor.getLong(mRowIdColumn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHasStableIds(boolean hasStableIds) {
|
||||
super.setHasStableIds(hasStableIds);
|
||||
}
|
||||
|
||||
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(VH viewHolder, int position) {
|
||||
if (!mDataValid) {
|
||||
throw new IllegalStateException("this should only be called when the cursor is valid");
|
||||
}
|
||||
if (!mCursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("couldn't move cursor to position " + position);
|
||||
}
|
||||
onBindViewHolder(viewHolder, mCursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
|
||||
* closed.
|
||||
*/
|
||||
public void changeCursor(Cursor cursor) {
|
||||
Cursor old = swapCursor(cursor);
|
||||
if (old != null) {
|
||||
old.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap in a new Cursor, returning the old Cursor. Unlike
|
||||
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
|
||||
* closed.
|
||||
*/
|
||||
public Cursor swapCursor(Cursor newCursor) {
|
||||
if (newCursor == mCursor) {
|
||||
return null;
|
||||
}
|
||||
// unregister observers on old cursor
|
||||
final Cursor oldCursor = mCursor;
|
||||
if (oldCursor != null) {
|
||||
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
|
||||
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
// register observers on the new cursor
|
||||
mCursor = newCursor;
|
||||
if (mCursor != null) {
|
||||
if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
|
||||
if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
|
||||
|
||||
mRowIdColumn = newCursor.getColumnIndexOrThrow(mIdIdentifier);
|
||||
mDataValid = true;
|
||||
} else {
|
||||
mRowIdColumn = -1;
|
||||
mDataValid = false;
|
||||
}
|
||||
|
||||
// notify that Dataset has Changed
|
||||
boolean notifyDataChanged = true;
|
||||
|
||||
// check if we can get better notifies
|
||||
if(mUseIndividualNotifies && oldCursor != null && newCursor != null && !oldCursor.isClosed() && !newCursor.isClosed()) {
|
||||
notifyDataChanged = notifyItems(oldCursor, newCursor);
|
||||
}
|
||||
|
||||
if(notifyDataChanged) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
return oldCursor;
|
||||
}
|
||||
|
||||
public void setUseIndividualNotifies(boolean use) {
|
||||
mUseIndividualNotifies = use;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the two cursors and calls notifyChanged on every item that changed.
|
||||
* @param oldCursor
|
||||
* @param newCursor
|
||||
* @return
|
||||
*/
|
||||
private boolean notifyItems(Cursor oldCursor, Cursor newCursor) {
|
||||
String[] columns = new String[] { BaseColumns._ID };
|
||||
CursorJoiner joiner = new CursorJoiner(oldCursor, columns, newCursor, columns);
|
||||
for (CursorJoiner.Result res : joiner) {
|
||||
switch (res) {
|
||||
case LEFT:
|
||||
notifyItemRemoved(newCursor.getPosition());
|
||||
break;
|
||||
case RIGHT:
|
||||
notifyItemInserted(newCursor.getPosition());
|
||||
break;
|
||||
case BOTH:
|
||||
// for(int i = 0; i < newCursor.getColumnCount(); i++) {
|
||||
// if(!oldCursor.getString(i).equals(newCursor.getString(i))) {
|
||||
// notifyItemChanged(newCursor.getPosition());
|
||||
// }
|
||||
// }
|
||||
if (getRowHash(oldCursor) != getRowHash(newCursor)) {
|
||||
notifyItemChanged(newCursor.getPosition());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getRowHash(Cursor cursor) {
|
||||
StringBuilder result = new StringBuilder("row");
|
||||
for (int i = 0; i < cursor.getColumnCount(); i++) {
|
||||
result.append(cursor.getString(i));
|
||||
}
|
||||
return result.toString().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the cursor into a CharSequence. Subclasses should override this
|
||||
* method to convert their results. The default implementation returns an
|
||||
* empty String for null values or the default String representation of
|
||||
* the value.</p>
|
||||
*
|
||||
* @param cursor the cursor to convert to a CharSequence
|
||||
* @return a CharSequence representing the value
|
||||
*/
|
||||
public CharSequence convertToString(Cursor cursor) {
|
||||
return cursor == null ? "" : cursor.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a query with the specified constraint. This query is requested
|
||||
* by the filter attached to this adapter.
|
||||
*
|
||||
* The query is provided by a
|
||||
* {@link FilterQueryProvider}.
|
||||
* If no provider is specified, the current cursor is not filtered and returned.
|
||||
*
|
||||
* After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
|
||||
* and the previous cursor is closed.
|
||||
*
|
||||
* This method is always executed on a background thread, not on the
|
||||
* application's main thread (or UI thread.)
|
||||
*
|
||||
* Contract: when constraint is null or empty, the original results,
|
||||
* prior to any filtering, must be returned.
|
||||
*
|
||||
* @param constraint the constraint with which the query must be filtered
|
||||
*
|
||||
* @return a Cursor representing the results of the new query
|
||||
*
|
||||
* @see #getFilter()
|
||||
* @see #getFilterQueryProvider()
|
||||
* @see #setFilterQueryProvider(FilterQueryProvider)
|
||||
*/
|
||||
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
|
||||
if (mFilterQueryProvider != null) {
|
||||
return mFilterQueryProvider.runQuery(constraint);
|
||||
}
|
||||
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
if (mCursorFilter == null) {
|
||||
mCursorFilter = new CursorFilter(this);
|
||||
}
|
||||
return mCursorFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query filter provider used for filtering. When the
|
||||
* provider is null, no filtering occurs.
|
||||
*
|
||||
* @return the current filter query provider or null if it does not exist
|
||||
*
|
||||
* @see #setFilterQueryProvider(FilterQueryProvider)
|
||||
* @see #runQueryOnBackgroundThread(CharSequence)
|
||||
*/
|
||||
public FilterQueryProvider getFilterQueryProvider() {
|
||||
return mFilterQueryProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query filter provider used to filter the current Cursor.
|
||||
* The provider's
|
||||
* {@link FilterQueryProvider#runQuery(CharSequence)}
|
||||
* method is invoked when filtering is requested by a client of
|
||||
* this adapter.
|
||||
*
|
||||
* @param filterQueryProvider the filter query provider or null to remove it
|
||||
*
|
||||
* @see #getFilterQueryProvider()
|
||||
* @see #runQueryOnBackgroundThread(CharSequence)
|
||||
*/
|
||||
public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
|
||||
mFilterQueryProvider = filterQueryProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link ContentObserver} on the cursor receives a change notification.
|
||||
* Can be implemented by sub-class.
|
||||
*
|
||||
* @see ContentObserver#onChange(boolean)
|
||||
*/
|
||||
protected void onContentChanged() {
|
||||
|
||||
}
|
||||
|
||||
private class ChangeObserver extends ContentObserver {
|
||||
public ChangeObserver() {
|
||||
super(new Handler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deliverSelfNotifications() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private class NotifyingDataSetObserver extends DataSetObserver {
|
||||
private final String TAG = NotifyingDataSetObserver.class.getSimpleName();
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
mDataValid = true;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
super.onInvalidated();
|
||||
mDataValid = false;
|
||||
notifyDataSetChanged();
|
||||
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.database;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseColumns;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.columns.ExercisesLocalColumns;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DBHandler extends SQLiteOpenHelper {
|
||||
|
||||
private SQLiteDatabase dataBase;
|
||||
private static final String DATABASE_NAME = "exercises.sqlite";
|
||||
private static final String DATABASE_PATH = "/data/data/org.secuso.privacyfriendlybreakreminder/databases/";
|
||||
private static final int DATABASE_VERSION = 3;
|
||||
|
||||
private static final String[] deleteQueryList = {
|
||||
ExerciseColumns.SQL_DELETE_ENTRIES,
|
||||
ExercisesLocalColumns.SQL_DELETE_ENTRIES};
|
||||
|
||||
public DBHandler(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
|
||||
//Check if database exists
|
||||
File databaseFile = context.getDatabasePath(DATABASE_NAME);
|
||||
if (!databaseFile.exists()) {
|
||||
this.getReadableDatabase();
|
||||
try {
|
||||
copyDataBase(context);
|
||||
this.close();
|
||||
} catch (Exception e) {
|
||||
Log.v("db log", "Copying data didn´t work!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
|
||||
}
|
||||
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
execSQLList(db, deleteQueryList);
|
||||
}
|
||||
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
onUpgrade(db, oldVersion, newVersion);
|
||||
}
|
||||
|
||||
private void execSQLList(SQLiteDatabase db, String[] queryList) {
|
||||
for (String query : queryList) {
|
||||
db.execSQL(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Cursor getExerciseCursor(String language) {
|
||||
SQLiteDatabase database = getReadableDatabase();
|
||||
|
||||
return database.rawQuery(buildQuery(false), new String[]{language});
|
||||
}
|
||||
|
||||
public List<Exercise> getExerciseList(String language) {
|
||||
SQLiteDatabase database = getReadableDatabase();
|
||||
|
||||
Cursor c = database.rawQuery(buildQuery(false), new String[]{language});
|
||||
|
||||
List<Exercise> result = new ArrayList<>();
|
||||
|
||||
if(c != null) {
|
||||
|
||||
while(!c.isAfterLast()) {
|
||||
result.add(ExerciseColumns.getExercise(c));
|
||||
c.moveToNext();
|
||||
}
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* SELECT
|
||||
* E._id,
|
||||
* E.section,
|
||||
* E.image_id,
|
||||
* L.local_id,
|
||||
* L.language,
|
||||
* L.exercise_id,
|
||||
* L.name,
|
||||
* L.description,
|
||||
* L.execution
|
||||
* FROM exercises E LEFT OUTER JOIN exercises_local L
|
||||
* ON E._id = L.exercise_id
|
||||
* 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) {
|
||||
StringBuilder sqlQuery = new StringBuilder();
|
||||
|
||||
sqlQuery.append("SELECT ");
|
||||
|
||||
for(String field : ExerciseColumns.PROJECTION) {
|
||||
sqlQuery.append("E.").append(field).append(", ");
|
||||
}
|
||||
for(String field : ExercisesLocalColumns.PROJECTION) {
|
||||
sqlQuery.append("L.").append(field).append(", ");
|
||||
}
|
||||
// delete the last comma
|
||||
sqlQuery.setLength(sqlQuery.length()-2);
|
||||
|
||||
sqlQuery.append(" FROM ");
|
||||
sqlQuery.append(ExerciseColumns.TABLE_NAME);
|
||||
sqlQuery.append(" E LEFT OUTER JOIN ");
|
||||
sqlQuery.append(ExercisesLocalColumns.TABLE_NAME);
|
||||
sqlQuery.append(" L");
|
||||
|
||||
sqlQuery.append("ON E.");
|
||||
sqlQuery.append(ExerciseColumns._ID);
|
||||
sqlQuery.append(" = L.");
|
||||
sqlQuery.append(ExercisesLocalColumns.EXERCISE_ID);
|
||||
|
||||
sqlQuery.append("WHERE ");
|
||||
sqlQuery.append("L.");
|
||||
sqlQuery.append(ExercisesLocalColumns.LANGUAGE);
|
||||
sqlQuery.append("= ? ");
|
||||
|
||||
if(addSectionCheck) {
|
||||
sqlQuery.append("AND E.");
|
||||
sqlQuery.append(ExerciseColumns.SECTION);
|
||||
sqlQuery.append("LIKE ? ");
|
||||
}
|
||||
|
||||
sqlQuery.append("ORDER BY E.");
|
||||
sqlQuery.append(ExerciseColumns._ID);
|
||||
sqlQuery.append(" ASC");
|
||||
|
||||
return sqlQuery.toString();
|
||||
}
|
||||
|
||||
|
||||
public List<Exercise> getExercisesFromSection(String language, String section) {
|
||||
SQLiteDatabase database = getReadableDatabase();
|
||||
|
||||
Cursor c = database.rawQuery(buildQuery(true), new String[]{language, "%"+section+"%"});
|
||||
|
||||
List<Exercise> result = new ArrayList<>();
|
||||
|
||||
if(c != null) {
|
||||
|
||||
while(!c.isAfterLast()) {
|
||||
result.add(ExerciseColumns.getExercise(c));
|
||||
c.moveToNext();
|
||||
}
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void copyDataBase(Context context) throws IOException {
|
||||
InputStream myInput = context.getAssets().open(DATABASE_NAME);
|
||||
String outFileName = DATABASE_PATH + DATABASE_NAME;
|
||||
OutputStream myOutput = new FileOutputStream(outFileName);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = myInput.read(buffer)) > 0) {
|
||||
myOutput.write(buffer, 0, length);
|
||||
}
|
||||
|
||||
myOutput.flush();
|
||||
myOutput.close();
|
||||
myInput.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.database;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseSetColumns;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseColumns;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.columns.ExerciseLocalColumns;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.ExerciseSet;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SQLiteHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String TAG = SQLiteHelper.class.getSimpleName();
|
||||
|
||||
private Context mContext;
|
||||
private static final String DATABASE_NAME = "exercises.sqlite";
|
||||
private static final String DATABASE_PATH = "/data/data/org.secuso.privacyfriendlybreakreminder/databases/";
|
||||
private static final int DATABASE_VERSION = 4;
|
||||
|
||||
private static final String[] deleteQueryList = {
|
||||
ExerciseColumns.SQL_DELETE_ENTRIES,
|
||||
ExerciseLocalColumns.SQL_DELETE_ENTRIES,
|
||||
ExerciseSetColumns.SQL_DELETE_ENTRIES};
|
||||
|
||||
private boolean onCreate;
|
||||
private boolean onUpgrade;
|
||||
|
||||
public SQLiteHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(SQLiteDatabase db) {
|
||||
if (onCreate || onUpgrade) {
|
||||
onCreate = onUpgrade = false;
|
||||
copyDatabaseFromAssets(db);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
onCreate = true;
|
||||
}
|
||||
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
onUpgrade = true;
|
||||
}
|
||||
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
onUpgrade(db, oldVersion, newVersion);
|
||||
}
|
||||
|
||||
|
||||
public synchronized Cursor getExerciseCursor(String language) {
|
||||
SQLiteDatabase database = getReadableDatabase();
|
||||
return database.rawQuery(buildQuery(false), new String[]{language});
|
||||
}
|
||||
|
||||
public synchronized List<Exercise> getExerciseList(String language) {
|
||||
Cursor c = getExerciseCursor(language);
|
||||
return buildExerciseList(c);
|
||||
}
|
||||
|
||||
public synchronized Cursor getExerciseCursorForSet(int setId, String language) {
|
||||
SQLiteDatabase database = getReadableDatabase();
|
||||
|
||||
String sql = "SELECT *\n" +
|
||||
"FROM exercise_set ES LEFT OUTER JOIN exercise_set_exercises ESE\n" +
|
||||
"\tON ES.exercise_set_id = ESE.exercise_set_id\n" +
|
||||
"LEFT OUTER JOIN exercises E\n" +
|
||||
"\tON ESE.exercise_id = E.exercise_id\n" +
|
||||
"LEFT OUTER JOIN exercises_local L\n" +
|
||||
"\tON E.exercise_id = L.exercise_id\n" +
|
||||
"WHERE ES.exercise_set_id = ? AND L.language = ?\n" +
|
||||
"ORDER BY ESE.exercise_id ASC";
|
||||
|
||||
String sql2 = "SELECT *\n" +
|
||||
"\tFROM (SELECT * \n" +
|
||||
"\t\t\tFROM (SELECT *\n" +
|
||||
"\t\t\t\tFROM "+ExerciseSetColumns.TABLE_NAME+" ES LEFT OUTER JOIN exercise_set_exercises ESE\n" +
|
||||
"\t\t\t\tON ES."+ExerciseSetColumns._ID+" = ESE."+ExerciseSetColumns._ID+"\n" +
|
||||
"\t\t\t\tWHERE ES."+ExerciseSetColumns._ID+" = ?\n" +
|
||||
"\t\t\t\tORDER BY ESE."+ExerciseColumns._ID+" ASC) ES_ESE \n" +
|
||||
"\t\t\tLEFT OUTER JOIN "+ExerciseColumns.TABLE_NAME+" E\n" +
|
||||
"\t\t\tON ES_ESE."+ExerciseColumns._ID+" = E."+ExerciseColumns._ID+") ES_ESE_E \n" +
|
||||
"\t\tLEFT OUTER JOIN "+ExerciseLocalColumns.TABLE_NAME+" L\n" +
|
||||
"\t\tON ES_ESE_E."+ExerciseColumns._ID+" = L."+ExerciseLocalColumns.EXERCISE_ID+"\n" +
|
||||
"\t\tWHERE L."+ExerciseLocalColumns.LANGUAGE+" = ?";
|
||||
|
||||
return database.rawQuery(sql, new String[]{String.valueOf(setId), language});
|
||||
}
|
||||
|
||||
public synchronized ExerciseSet getExerciseListForSet(int setId, String language) {
|
||||
Cursor c = getExerciseCursorForSet(setId, language);
|
||||
|
||||
ExerciseSet result = null;
|
||||
|
||||
if(c != null) {
|
||||
|
||||
c.moveToFirst();
|
||||
|
||||
result = ExerciseSetColumns.fromCursor(c);
|
||||
|
||||
while(!c.isAfterLast()) {
|
||||
result.add(ExerciseColumns.fromCursor(c));
|
||||
c.moveToNext();
|
||||
}
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Exercise> buildExerciseList(Cursor c) {
|
||||
List<Exercise> result = new ArrayList<>();
|
||||
|
||||
if(c != null) {
|
||||
|
||||
c.moveToFirst();
|
||||
|
||||
while(!c.isAfterLast()) {
|
||||
result.add(ExerciseColumns.fromCursor(c));
|
||||
c.moveToNext();
|
||||
}
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private String buildQuery(boolean addSectionCheck) {
|
||||
return buildQuery(addSectionCheck, "");
|
||||
}
|
||||
|
||||
public synchronized List<Exercise> getExercisesFromSection(String language, String section) { // TODO: Rename after old activities are deleted
|
||||
SQLiteDatabase database = getReadableDatabase();
|
||||
|
||||
Cursor c = database.rawQuery(buildQuery(true), 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
|
||||
* FROM exercises E LEFT OUTER JOIN exercises_local L
|
||||
* ON E._id = L.exercise_id
|
||||
* 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) {
|
||||
StringBuilder sqlQuery = new StringBuilder();
|
||||
|
||||
sqlQuery.append("SELECT ");
|
||||
|
||||
for(String field : ExerciseColumns.PROJECTION) {
|
||||
sqlQuery.append("E.").append(field).append(", ");
|
||||
}
|
||||
for(String field : ExerciseLocalColumns.PROJECTION) {
|
||||
sqlQuery.append("L.").append(field).append(", ");
|
||||
}
|
||||
// delete the last comma
|
||||
sqlQuery.setLength(sqlQuery.length()-2);
|
||||
|
||||
sqlQuery.append(" FROM ");
|
||||
sqlQuery.append(ExerciseColumns.TABLE_NAME);
|
||||
sqlQuery.append(" E LEFT OUTER JOIN ");
|
||||
sqlQuery.append(ExerciseLocalColumns.TABLE_NAME);
|
||||
sqlQuery.append(" L ");
|
||||
|
||||
sqlQuery.append("ON E.");
|
||||
sqlQuery.append(ExerciseColumns._ID);
|
||||
sqlQuery.append(" = L.");
|
||||
sqlQuery.append(ExerciseLocalColumns.EXERCISE_ID);
|
||||
sqlQuery.append(" ");
|
||||
|
||||
sqlQuery.append("WHERE ");
|
||||
sqlQuery.append("L.");
|
||||
sqlQuery.append(ExerciseLocalColumns.LANGUAGE);
|
||||
sqlQuery.append(" = ? ");
|
||||
|
||||
if(addSectionCheck) {
|
||||
sqlQuery.append("AND E.");
|
||||
sqlQuery.append(ExerciseColumns.SECTION);
|
||||
sqlQuery.append(" LIKE ? ");
|
||||
}
|
||||
|
||||
sqlQuery.append("ORDER BY E.");
|
||||
sqlQuery.append(ExerciseColumns._ID);
|
||||
sqlQuery.append(" ASC");
|
||||
|
||||
return sqlQuery.toString();
|
||||
}
|
||||
|
||||
private void copyDataBase(Context context) throws IOException {
|
||||
InputStream myInput = context.getAssets().open(DATABASE_NAME);
|
||||
String outFileName = DATABASE_PATH + DATABASE_NAME;
|
||||
OutputStream myOutput = new FileOutputStream(outFileName);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = myInput.read(buffer)) > 0) {
|
||||
myOutput.write(buffer, 0, length);
|
||||
}
|
||||
|
||||
myOutput.flush();
|
||||
myOutput.close();
|
||||
myInput.close();
|
||||
|
||||
SQLiteDatabase copiedDb = context.openOrCreateDatabase(DATABASE_NAME, 0, null);
|
||||
copiedDb.execSQL("PRAGMA user_version = " + DATABASE_VERSION);
|
||||
copiedDb.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy packaged database from assets folder to the database created in the
|
||||
* application package context.
|
||||
*
|
||||
* @param db
|
||||
* The target database in the application package context.
|
||||
*/
|
||||
private void copyDatabaseFromAssets(SQLiteDatabase db) {
|
||||
Log.i(TAG, "copyDatabase");
|
||||
InputStream myInput = null;
|
||||
OutputStream myOutput = null;
|
||||
try {
|
||||
// Open db packaged as asset as the input stream
|
||||
myInput = mContext.getAssets().open(DATABASE_NAME);
|
||||
|
||||
// Open the db in the application package context:
|
||||
myOutput = new FileOutputStream(db.getPath());
|
||||
|
||||
// Transfer db file contents:
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = myInput.read(buffer)) > 0) {
|
||||
myOutput.write(buffer, 0, length);
|
||||
}
|
||||
myOutput.flush();
|
||||
|
||||
// Set the version of the copied database to the current
|
||||
// version:
|
||||
SQLiteDatabase copiedDb = mContext.openOrCreateDatabase(DATABASE_NAME, 0, null);
|
||||
copiedDb.execSQL("PRAGMA user_version = " + DATABASE_VERSION);
|
||||
copiedDb.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new Error(TAG + " Error copying database");
|
||||
} finally {
|
||||
// Close the streams
|
||||
try {
|
||||
if (myOutput != null) {
|
||||
myOutput.close();
|
||||
}
|
||||
if (myInput != null) {
|
||||
myInput.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new Error(TAG + " Error closing streams");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package org.secuso.privacyfriendlybreakreminder.database.columns;
|
|||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
||||
|
||||
|
|
@ -10,10 +9,11 @@ import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
|||
* Created by Christopher Beckmann on 23.08.2017.
|
||||
*/
|
||||
|
||||
public class ExerciseColumns implements BaseColumns {
|
||||
public final class ExerciseColumns {
|
||||
|
||||
public static final String TABLE_NAME = "exercises";
|
||||
|
||||
public static final String _ID = "exercise_id";
|
||||
public static final String SECTION = "section";
|
||||
public static final String IMAGE_ID = "image_id";
|
||||
|
||||
|
|
@ -24,8 +24,8 @@ public class ExerciseColumns implements BaseColumns {
|
|||
};
|
||||
public static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
|
||||
public static Exercise getExercise(Cursor c) {
|
||||
Exercise e = ExercisesLocalColumns.getExercise(c);
|
||||
public static Exercise fromCursor(Cursor c) {
|
||||
Exercise e = ExerciseLocalColumns.fromCursor(c);
|
||||
|
||||
e.setId(c.getInt(c.getColumnIndexOrThrow(ExerciseColumns._ID)));
|
||||
e.setSection(c.getString(c.getColumnIndexOrThrow(ExerciseColumns.SECTION)));
|
||||
|
|
@ -42,8 +42,10 @@ public class ExerciseColumns implements BaseColumns {
|
|||
values.put(ExerciseColumns.SECTION, record.getSection());
|
||||
values.put(ExerciseColumns.IMAGE_ID, record.getImageID());
|
||||
|
||||
values.putAll(ExercisesLocalColumns.getValues(record));
|
||||
values.putAll(ExerciseLocalColumns.getValues(record));
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private ExerciseColumns() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package org.secuso.privacyfriendlybreakreminder.database.columns;
|
|||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
||||
|
||||
|
|
@ -10,7 +9,7 @@ import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
|||
* Created by Christopher Beckmann on 25.08.2017.
|
||||
*/
|
||||
|
||||
public class ExercisesLocalColumns implements BaseColumns {
|
||||
public final class ExerciseLocalColumns {
|
||||
|
||||
public static final String TABLE_NAME = "exercises_local";
|
||||
|
||||
|
|
@ -31,14 +30,14 @@ public class ExercisesLocalColumns implements BaseColumns {
|
|||
};
|
||||
public static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
|
||||
public static Exercise getExercise(Cursor c) {
|
||||
public static Exercise fromCursor(Cursor c) {
|
||||
Exercise e = new Exercise();
|
||||
|
||||
e.setLocalId(c.getInt(c.getColumnIndexOrThrow(ExercisesLocalColumns._ID)));
|
||||
e.setLanguage(c.getString(c.getColumnIndexOrThrow(ExercisesLocalColumns.LANGUAGE)));
|
||||
e.setDescription(c.getString(c.getColumnIndexOrThrow(ExercisesLocalColumns.DESCRIPTION)));
|
||||
e.setExecution(c.getString(c.getColumnIndexOrThrow(ExercisesLocalColumns.EXECUTION)));
|
||||
e.setName(c.getString(c.getColumnIndexOrThrow(ExercisesLocalColumns.NAME)));
|
||||
e.setLocalId(c.getInt(c.getColumnIndexOrThrow(ExerciseLocalColumns._ID)));
|
||||
e.setLanguage(c.getString(c.getColumnIndexOrThrow(ExerciseLocalColumns.LANGUAGE)));
|
||||
e.setDescription(c.getString(c.getColumnIndexOrThrow(ExerciseLocalColumns.DESCRIPTION)));
|
||||
e.setExecution(c.getString(c.getColumnIndexOrThrow(ExerciseLocalColumns.EXECUTION)));
|
||||
e.setName(c.getString(c.getColumnIndexOrThrow(ExerciseLocalColumns.NAME)));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
|
@ -47,13 +46,15 @@ public class ExercisesLocalColumns implements BaseColumns {
|
|||
ContentValues values = new ContentValues();
|
||||
|
||||
if(record.getLocalId() != -1) {
|
||||
values.put(ExercisesLocalColumns._ID, record.getLocalId());
|
||||
values.put(ExerciseLocalColumns._ID, record.getLocalId());
|
||||
}
|
||||
values.put(ExercisesLocalColumns.LANGUAGE, record.getLanguage());
|
||||
values.put(ExercisesLocalColumns.DESCRIPTION, record.getDescription());
|
||||
values.put(ExercisesLocalColumns.EXECUTION, record.getExecution());
|
||||
values.put(ExercisesLocalColumns.NAME, record.getName());
|
||||
values.put(ExerciseLocalColumns.LANGUAGE, record.getLanguage());
|
||||
values.put(ExerciseLocalColumns.DESCRIPTION, record.getDescription());
|
||||
values.put(ExerciseLocalColumns.EXECUTION, record.getExecution());
|
||||
values.put(ExerciseLocalColumns.NAME, record.getName());
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private ExerciseLocalColumns() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.database.columns;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.Exercise;
|
||||
import org.secuso.privacyfriendlybreakreminder.database.data.ExerciseSet;
|
||||
|
||||
/**
|
||||
* Created by Christopher Beckmann on 03.09.2017.
|
||||
*/
|
||||
|
||||
public final class ExerciseSetColumns {
|
||||
|
||||
public static final String TABLE_NAME = "exercise_set";
|
||||
|
||||
public static final String _ID = "exercise_set_id";
|
||||
public static final String NAME = "name";
|
||||
|
||||
public static final String[] PROJECTION = {
|
||||
_ID,
|
||||
NAME
|
||||
};
|
||||
|
||||
public static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
|
||||
public static ExerciseSet fromCursor(Cursor c) {
|
||||
ExerciseSet e = new ExerciseSet();
|
||||
|
||||
e.setId(c.getInt(c.getColumnIndexOrThrow(ExerciseSetColumns._ID)));
|
||||
e.setName(c.getString(c.getColumnIndexOrThrow(ExerciseSetColumns.NAME)));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
public static ContentValues getValues(ExerciseSet record) {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
if(record.getId() != -1) {
|
||||
values.put(ExerciseSetColumns._ID, record.getId());
|
||||
}
|
||||
values.put(ExerciseSetColumns.NAME, record.getName());
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private ExerciseSetColumns() {}
|
||||
}
|
||||
|
|
@ -4,12 +4,12 @@ package org.secuso.privacyfriendlybreakreminder.database.data;
|
|||
public class Exercise {
|
||||
private int id;
|
||||
private int localId;
|
||||
private String section;
|
||||
private String execution;
|
||||
private String description;
|
||||
private String name;
|
||||
private String section = "";
|
||||
private String execution = "";
|
||||
private String description = "";
|
||||
private String name = "";
|
||||
private String imageID;
|
||||
private String language;
|
||||
private String language = "";
|
||||
|
||||
public Exercise() {
|
||||
this.localId = -1;
|
||||
|
|
@ -60,4 +60,35 @@ public class Exercise {
|
|||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(id)
|
||||
.append(localId)
|
||||
.append(section)
|
||||
.append(execution)
|
||||
.append(description)
|
||||
.append(name)
|
||||
.append(imageID)
|
||||
.append(language);
|
||||
|
||||
return sb.toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if(!(object instanceof Exercise)) return false;
|
||||
Exercise other = (Exercise) object;
|
||||
|
||||
return this.id != other.id
|
||||
|| this.localId != other.localId
|
||||
|| !this.section.equals(other.section)
|
||||
|| !this.execution.equals(other.execution)
|
||||
|| !this.description.equals(other.description)
|
||||
|| !this.name.equals(other.name)
|
||||
|| !this.imageID.equals(other.imageID)
|
||||
|| !this.language.equals(other.language);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package org.secuso.privacyfriendlybreakreminder.database.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Christopher Beckmann on 03.09.2017.
|
||||
*/
|
||||
|
||||
public class ExerciseSet {
|
||||
private int id = -1;
|
||||
private String name = null;
|
||||
private List<Exercise> exercises = new ArrayList<>();
|
||||
|
||||
public ExerciseSet() {}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void add(Exercise exercise) {
|
||||
if(!exercises.contains(exercise)) {
|
||||
exercises.add(exercise);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(Exercise exercise) {
|
||||
if(exercises.contains(exercise)) {
|
||||
exercises.remove(exercise);
|
||||
}
|
||||
}
|
||||
|
||||
public void get(int index) {
|
||||
exercises.get(index);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return exercises.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import org.secuso.privacyfriendlybreakreminder.R;
|
|||
import org.secuso.privacyfriendlybreakreminder.activities.TimerActivity;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.Locale;
|
||||
import java.util.Timer;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
|
|
@ -74,6 +75,7 @@ public class TimerService extends Service {
|
|||
mTimer = createTimer(duration);
|
||||
mTimer.start();
|
||||
isRunning = true;
|
||||
sendBroadcast(buildBroadcast());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +83,7 @@ public class TimerService extends Service {
|
|||
if(isRunning) {
|
||||
mTimer.cancel();
|
||||
isRunning = false;
|
||||
sendBroadcast(buildBroadcast());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,17 +92,29 @@ public class TimerService extends Service {
|
|||
mTimer = createTimer(remainingDuration);
|
||||
mTimer.start();
|
||||
isRunning = true;
|
||||
sendBroadcast(buildBroadcast());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void resetTimer() {
|
||||
if(isRunning) {
|
||||
mTimer.cancel();
|
||||
isRunning = false;
|
||||
remainingDuration = 0;
|
||||
mTimer = createTimer(initialDuration);
|
||||
mTimer.start();
|
||||
}
|
||||
remainingDuration = initialDuration;
|
||||
sendBroadcast(buildBroadcast());
|
||||
}
|
||||
|
||||
public synchronized void stopAndResetTimer() {
|
||||
if(isRunning) mTimer.cancel();
|
||||
isRunning = false;
|
||||
remainingDuration = initialDuration;
|
||||
sendBroadcast(buildBroadcast());
|
||||
}
|
||||
|
||||
public synchronized boolean isPaused() { return !isRunning && remainingDuration > 0 && remainingDuration != initialDuration; }
|
||||
|
||||
public synchronized boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
|
@ -111,29 +126,36 @@ public class TimerService extends Service {
|
|||
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
int secondsUntilFinished = (int) Math.ceil(millisUntilFinished / 1000.0);
|
||||
synchronized (TimerService.this) {
|
||||
remainingDuration = millisUntilFinished;
|
||||
}
|
||||
|
||||
remainingDuration = millisUntilFinished;
|
||||
|
||||
Intent broadcast = new Intent(TIMER_BROADCAST);
|
||||
broadcast.putExtra("onTickMillis", millisUntilFinished);
|
||||
broadcast.putExtra("countdown_seconds", secondsUntilFinished);
|
||||
sendBroadcast(broadcast);
|
||||
sendBroadcast(buildBroadcast());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// TODO: finish broadcast
|
||||
Intent broadcast = new Intent(TIMER_BROADCAST);
|
||||
Intent broadcast = buildBroadcast();
|
||||
broadcast.putExtra("done", true);
|
||||
broadcast.putExtra("onTickMillis", 0);
|
||||
broadcast.putExtra("countdown_seconds", 0);
|
||||
sendBroadcast(broadcast);
|
||||
resetTimer();
|
||||
sendBroadcast(buildBroadcast());
|
||||
|
||||
stopAndResetTimer();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private synchronized Intent buildBroadcast() {
|
||||
int secondsUntilFinished = (int) Math.ceil(remainingDuration / 1000.0);
|
||||
|
||||
Intent broadcast = new Intent(TIMER_BROADCAST);
|
||||
broadcast.putExtra("onTickMillis", remainingDuration);
|
||||
broadcast.putExtra("initialMillis", initialDuration);
|
||||
broadcast.putExtra("countdown_seconds", secondsUntilFinished);
|
||||
broadcast.putExtra("isRunning", isRunning());
|
||||
broadcast.putExtra("isPaused", isPaused());
|
||||
return (broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
|
|
@ -157,15 +179,9 @@ public class TimerService extends Service {
|
|||
int seconds = secondsUntilFinished % 60;
|
||||
int minutes = minutesUntilFinished % 60;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String time = String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);
|
||||
|
||||
if(hours > 0) sb.append(hours).append(":");
|
||||
if(minutes < 10) sb.append(0);
|
||||
sb.append(minutes).append(":");
|
||||
if(seconds < 10) sb.append(0);
|
||||
sb.append(seconds);
|
||||
|
||||
builder.setContentText(sb.toString());
|
||||
builder.setContentText(time);
|
||||
builder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, TimerActivity.class), FLAG_UPDATE_CURRENT));
|
||||
builder.setColor(ContextCompat.getColor(this, R.color.colorAccent));
|
||||
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
|
|
@ -189,6 +205,10 @@ public class TimerService extends Service {
|
|||
context.startService(new Intent(context.getApplicationContext(), TimerService.class));
|
||||
}
|
||||
|
||||
public synchronized long getRemainingDuration() {
|
||||
return remainingDuration;
|
||||
}
|
||||
|
||||
public class TimerServiceBinder extends Binder {
|
||||
public TimerService getService() {
|
||||
return TimerService.this;
|
||||
|
|
|
|||
BIN
app/src/main/res/drawable-hdpi/ic_play_arrow_black_48dp.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_play_arrow_black_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 265 B |
BIN
app/src/main/res/drawable-mdpi/ic_play_arrow_black_48dp.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_play_arrow_black_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 208 B |
BIN
app/src/main/res/drawable-xhdpi/ic_play_arrow_black_48dp.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_play_arrow_black_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 320 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_play_arrow_black_48dp.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_play_arrow_black_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 394 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_arrow_black_48dp.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_arrow_black_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 515 B |
22
app/src/main/res/drawable/circular_small.xml
Normal file
22
app/src/main/res/drawable/circular_small.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape
|
||||
android:innerRadiusRatio="2"
|
||||
android:shape="ring"
|
||||
android:useLevel="false"
|
||||
android:thicknessRatio="6">
|
||||
<solid android:color="@color/middlegrey"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<shape
|
||||
android:innerRadiusRatio="2"
|
||||
android:useLevel="true"
|
||||
android:shape="ring"
|
||||
android:thicknessRatio="6">
|
||||
<solid android:color="@color/lightblue"/>
|
||||
<corners android:radius="2dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
152
app/src/main/res/layout/activity_exercise.xml
Normal file
152
app/src/main/res/layout/activity_exercise.xml
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.secuso.privacyfriendlybreakreminder.activities.ExerciseActivity">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_playPause"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:hapticFeedbackEnabled="true"
|
||||
android:onClick="onClick"
|
||||
android:scaleType="fitXY"
|
||||
android:tint="@color/darkblue"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:srcCompat="@drawable/ic_play_arrow_black_48dp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/relativeLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="125dp"
|
||||
android:layout_height="125dp"
|
||||
android:layout_alignTop="@+id/timerText"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:max="100"
|
||||
android:padding="16dp"
|
||||
android:progress="66"
|
||||
android:progressDrawable="@drawable/circular_small"
|
||||
android:rotation="270" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timerText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"
|
||||
android:text="00:00"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/exercise_image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
app:srcCompat="@drawable/exercise_0"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/relativeLayout"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/execution_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="Execution"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toRightOf="@+id/relativeLayout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/section_card" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/execution"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="sitting or standing"
|
||||
app:layout_constraintLeft_toRightOf="@+id/relativeLayout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/execution_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Description"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exercise_image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="- do something\n- do something else\n- then do the first something again"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/description_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_title" />
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="@color/middleblue"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:id="@+id/section_card"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/section"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:gravity="center"
|
||||
android:text="Neck, Arms"
|
||||
android:textColor="@color/white" />
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
36
app/src/main/res/layout/activity_exercise_set.xml
Normal file
36
app/src/main/res/layout/activity_exercise_set.xml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.secuso.privacyfriendlybreakreminder.activities.ExerciseSetOverviewActivity">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exercise_set_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_marginStart="8dp" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/exercise_list"
|
||||
android:layout_width="368dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exercise_set_name"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5">
|
||||
|
||||
</android.support.v7.widget.RecyclerView>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
|
@ -4,33 +4,112 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/activity_horizontal_margin"
|
||||
android:padding="0dp"
|
||||
android:layout_margin="0dp"
|
||||
tools:context="org.secuso.privacyfriendlybreakreminder.activities.TimerActivity">
|
||||
|
||||
<include android:id="@+id/exercise" layout="@layout/layout_exercise_set"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/button_chooseExercise"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clickable="true"
|
||||
app:fabSize="mini"
|
||||
android:onClick="onClick"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/exercise"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/relativeLayout"
|
||||
app:layout_constraintVertical_bias="0.25" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:id="@+id/relativeLayout"
|
||||
android:layout_marginEnd="8dp">
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_marginTop="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/exercise">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/picker_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:divider="@color/colorAccent"
|
||||
android:dividerPadding="4dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/hours_picker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="135dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:theme="@style/AppTheme.NumberPicker" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=":"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:theme="@style/AppTheme.NumberPicker" />
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/minutes_picker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="135dp"
|
||||
android:theme="@style/AppTheme.NumberPicker" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=":"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:theme="@style/AppTheme.NumberPicker" />
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/seconds_picker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="135dp"
|
||||
android:clipChildren="false"
|
||||
android:theme="@style/AppTheme.NumberPicker" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="250dp"
|
||||
android:layout_alignTop="@+id/timerText"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:max="100"
|
||||
android:onClick="onClick"
|
||||
android:padding="16dp"
|
||||
android:progress="66"
|
||||
android:progressDrawable="@drawable/circular"
|
||||
android:rotation="270"
|
||||
android:max="100"
|
||||
android:progress="66"
|
||||
android:layout_alignTop="@+id/timerText" />
|
||||
android:visibility="invisible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timerText"
|
||||
|
|
@ -39,21 +118,24 @@
|
|||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"
|
||||
android:text="00:00"
|
||||
android:text="00:00:00"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold" />
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/imageButton3"
|
||||
android:id="@+id/button_reset"
|
||||
android:layout_width="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_replay_black_48dp"
|
||||
android:hapticFeedbackEnabled="true"
|
||||
android:tint="@color/darkblue"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/relativeLayout"
|
||||
|
|
@ -61,15 +143,19 @@
|
|||
app:layout_constraintRight_toRightOf="@+id/relativeLayout"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/relativeLayout"
|
||||
app:layout_constraintHorizontal_bias="0.33" />
|
||||
app:layout_constraintHorizontal_bias="0.33"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/imageButton4"
|
||||
android:id="@+id/button_playPause"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:hapticFeedbackEnabled="true"
|
||||
android:scaleType="fitXY"
|
||||
android:tint="@color/darkblue"
|
||||
app:srcCompat="@drawable/ic_play_arrow_black_48dp"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintRight_toRightOf="@+id/relativeLayout"
|
||||
|
|
@ -77,6 +163,8 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/relativeLayout"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/relativeLayout"
|
||||
app:layout_constraintHorizontal_bias="0.66" />
|
||||
app:layout_constraintHorizontal_bias="0.66"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
|
|
|||
81
app/src/main/res/layout/layout_exercise.xml
Normal file
81
app/src/main/res/layout/layout_exercise.xml
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="0dp"
|
||||
android:padding="0dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exercise_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="Name"
|
||||
android:textAlignment="viewStart"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toRightOf="@+id/cardView2"
|
||||
app:layout_constraintTop_toTopOf="@+id/cardView2" />
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/cardView2"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:innerRadius="0dp"
|
||||
android:shape="ring"
|
||||
android:thicknessRatio="1.9"
|
||||
app:cardCornerRadius="24dp"
|
||||
app:cardElevation="0dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/exercise_image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/exercise_0" />
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exercise_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Description"
|
||||
android:textAlignment="textEnd"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exercise_execution"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Execution"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/exercise_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exercise_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exercise_section"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Section"
|
||||
android:textAlignment="textEnd"
|
||||
app:layout_constraintRight_toRightOf="@+id/exercise_description"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exercise_description" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
68
app/src/main/res/layout/layout_exercise_set.xml
Normal file
68
app/src/main/res/layout/layout_exercise_set.xml
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="132dp"
|
||||
android:padding="0dp"
|
||||
android:layout_margin="0dp"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/exercise_card"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginLeft="40dp"
|
||||
android:layout_marginRight="40dp"
|
||||
android:padding="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="visible"
|
||||
app:cardCornerRadius="2dp"
|
||||
app:cardElevation="4dp"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView9"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintLeft_toRightOf="@+id/cardView2"
|
||||
app:layout_constraintTop_toTopOf="@+id/cardView2" />
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/cardView2"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:innerRadius="0dp"
|
||||
android:shape="ring"
|
||||
android:thicknessRatio="1.9"
|
||||
app:cardCornerRadius="24dp"
|
||||
app:cardElevation="0dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/exercise_image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/exercise_0" />
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
|
@ -13,6 +13,13 @@
|
|||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NumberPicker" parent="AppTheme">
|
||||
<item name="android:textColorPrimary">@color/colorPrimary</item>
|
||||
<item name="android:textColorSecondary">@color/colorAccent</item>
|
||||
<item name="android:textSize">28sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue