Added random exercise set option for schedules. Also fixed annoying repeating notification sound when the timer was running. Restructure animations.

Closes #24
This commit is contained in:
Christopher Beckmann 2018-09-18 17:43:15 +02:00
commit 4848fdd301
12 changed files with 110 additions and 38 deletions

View file

@ -42,7 +42,9 @@ import org.secuso.privacyfriendlypausinghealthily.dialog.ExerciseDialog;
import org.secuso.privacyfriendlypausinghealthily.exercises.ExerciseLocale;
import org.secuso.privacyfriendlypausinghealthily.service.TimerService;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.Gravity.CENTER_HORIZONTAL;
@ -88,6 +90,7 @@ public class ExerciseActivity extends AppCompatActivity implements android.suppo
private boolean showBigTimer = false;
private boolean showControlButtons = true;
private boolean keepScreenOn = true;
private boolean scheduledExercise = false;
// exerciseSet info
private long exerciseSetId;
@ -125,16 +128,26 @@ public class ExerciseActivity extends AppCompatActivity implements android.suppo
exerciseSetId = pref.getLong(FirstLaunchManager.DEFAULT_EXERCISE_SET, 0L);
pauseDuration = pref.getLong(FirstLaunchManager.PAUSE_TIME, 5 * 60 * 1000);
repeatStatus = pref.getBoolean(FirstLaunchManager.REPEAT_STATUS, false);
keepScreenOn = pref.getBoolean(FirstLaunchManager.KEEP_SCREEN_ON_DURING_EXERCISE, true);
continuousStatus = pref.getBoolean(FirstLaunchManager.REPEAT_EXERCISES, false);
try {
exerciseTime = Long.parseLong(pref.getString(FirstLaunchManager.EXERCISE_DURATION, "30")) * 1000;
} catch (NumberFormatException e) {
exerciseTime = 30L * 1000;
}
keepScreenOn = pref.getBoolean(FirstLaunchManager.KEEP_SCREEN_ON_DURING_EXERCISE, true);
initResources();
// this must be called after init resources because the database is needed
// TODO: this call should probably not be done on the UI thread
boolean randomScheduleExercise = pref.getBoolean(FirstLaunchManager.PREF_SCHEDULE_RANDOM_EXERCISE, false);
scheduledExercise = getIntent().getBooleanExtra("SCHEDULED", false);
if(scheduledExercise && randomScheduleExercise) {
List<ExerciseSet> set = dbHelper.getExerciseSets(pref.getBoolean(FirstLaunchManager.PREF_HIDE_DEFAULT_SETS, false));
exerciseSetId = set.get((new Random()).nextInt(set.size())).getId(); // random from available sets
}
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
@ -229,6 +242,7 @@ public class ExerciseActivity extends AppCompatActivity implements android.suppo
if(pref.getBoolean(FirstLaunchManager.PREF_EXERCISE_CONTINUOUS, false)) {
Intent timerServiceIntent = new Intent(this.getApplicationContext(), TimerService.class);
timerServiceIntent.setAction(TimerService.ACTION_START_TIMER);
timerServiceIntent.putExtra("SCHEDULE", scheduledExercise);
startService(timerServiceIntent);
}

View file

@ -1,7 +1,5 @@
package org.secuso.privacyfriendlypausinghealthily.activities;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@ -76,9 +74,12 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap
private boolean isActivityVisible = false;
// animation
private int mShortAnimationDuration;
private boolean currentStatusIsPickerVisible = false;
private ExerciseSet currentExerciseSet;
private ConstraintSet constraintSetPicker;
private ConstraintSet constraintSetRunning;
// Service
private TimerService mTimerService = null;
@ -96,7 +97,6 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap
mTimerService = null;
}
};
private ExerciseSet currentExerciseSet;
private void onServiceConnected() {
updateUI();
@ -126,9 +126,28 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap
setContentView(R.layout.activity_timer);
initResources();
initAnimations();
getSupportLoaderManager().initLoader(0, null, this);
}
/**
* Must be called <b>after</b> {@link #initResources()}
*/
private void initAnimations() {
constraintSetPicker = new ConstraintSet();
constraintSetPicker.clone(mainContent);
constraintSetRunning = new ConstraintSet();
constraintSetRunning.clone(mainContent);
int[] chainViews = {R.id.button_reset, R.id.button_playPause};
float[] chainWeights = {0.5f, 0.5f};
constraintSetRunning.createHorizontalChain(0, ConstraintSet.LEFT, 0, ConstraintSet.RIGHT, chainViews, chainWeights, ConstraintSet.CHAIN_PACKED);
constraintSetRunning.setVisibility(R.id.button_reset, View.VISIBLE);
constraintSetRunning.setVisibility(R.id.picker_layout, View.INVISIBLE);
constraintSetRunning.setVisibility(R.id.progressBar, View.VISIBLE);
constraintSetRunning.setVisibility(R.id.timerText, View.VISIBLE);
}
@Override
protected int getNavigationDrawerID() {
return R.id.nav_timer;
@ -188,8 +207,6 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap
private void initResources() {
final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
exerciseSetAdapter = new ExerciseSetSpinnerAdapter(this, R.layout.layout_exercise_set, new LinkedList<ExerciseSet>());
mainContent = findViewById(R.id.main_content);
@ -391,25 +408,8 @@ public class TimerActivity extends BaseActivity implements android.support.v4.ap
private synchronized void showPicker(final boolean showPicker) {
if(showPicker != currentStatusIsPickerVisible) {
ConstraintSet constraintSet1 = new ConstraintSet();
constraintSet1.clone(mainContent);
constraintSet1.setHorizontalBias(R.id.button_playPause, 0.5f);
constraintSet1.setVisibility(R.id.button_reset, View.INVISIBLE);
constraintSet1.setVisibility(R.id.picker_layout, View.VISIBLE);
constraintSet1.setVisibility(R.id.progressBar, View.INVISIBLE);
constraintSet1.setVisibility(R.id.timerText, View.INVISIBLE);
ConstraintSet constraintSet2 = new ConstraintSet();
constraintSet2.clone(mainContent);
constraintSet2.setHorizontalBias(R.id.button_playPause, 0.66f);
constraintSet2.setVisibility(R.id.button_reset, View.VISIBLE);
constraintSet2.setVisibility(R.id.picker_layout, View.INVISIBLE);
constraintSet2.setVisibility(R.id.progressBar, View.VISIBLE);
constraintSet2.setVisibility(R.id.timerText, View.VISIBLE);
TransitionManager.beginDelayedTransition(mainContent);
ConstraintSet constraint = showPicker ? constraintSet1 : constraintSet2;
ConstraintSet constraint = showPicker ? constraintSetPicker : constraintSetRunning;
constraint.applyTo(mainContent);
//pickerLayout.clearAnimation();

View file

@ -44,6 +44,7 @@ public class FirstLaunchManager {
public static final String PREF_EXERCISE_CONTINUOUS = "pref_exercise_continuous";
public static final String PREF_HIDE_DEFAULT_SETS = "pref_hide_default_exercise_sets";
public static final String WORK_TIME = "WORK_TIME";
public static final String PREF_SCHEDULE_RANDOM_EXERCISE= "pref_schedule_random_exercise";
private final SQLiteHelper dbHandler;
private Context context;
@ -85,6 +86,7 @@ public class FirstLaunchManager {
.putLong(PREF_SCHEDULE_EXERCISE_TIME, 32400000L)
.putBoolean(KEEP_SCREEN_ON_DURING_EXERCISE, true)
.putBoolean(PREF_EXERCISE_CONTINUOUS, false)
.putBoolean(PREF_SCHEDULE_RANDOM_EXERCISE, false)
.putStringSet(PREF_SCHEDULE_EXERCISE_DAYS, new HashSet<String>(Arrays.asList("Mo", "Di", "Mi", "Do", "Fr", "Sa", "So")))
.apply();
@ -108,6 +110,7 @@ public class FirstLaunchManager {
// channels
NotificationChannel timerRunningChannel = new NotificationChannel("timer_running", "Timer Running Notification", NotificationManager.IMPORTANCE_DEFAULT);
timerRunningChannel.setVibrationPattern(new long[] { 0 });
timerRunningChannel.setSound(null, null);
timerRunningChannel.setGroup(groupId);
NotificationChannel timerDoneChannel = new NotificationChannel("timer_done", "Timer Done Notification", NotificationManager.IMPORTANCE_HIGH);

View file

@ -181,6 +181,29 @@ public class SQLiteHelper extends SQLiteAssetHelper {
return result;
}
public synchronized List<ExerciseSet> getExerciseSets(boolean hideDefaults) {
Cursor c = getExerciseSetsCursor();
List<ExerciseSet> result = new ArrayList<>();
if(c != null) {
c.moveToFirst();
while(!c.isAfterLast()) {
ExerciseSet set = ExerciseSetColumns.fromCursor(c);
if(!hideDefaults || !set.isDefaultSet()) {
result.add(set);
}
c.moveToNext();
}
c.close();
}
return result;
}
private List<Exercise> buildExerciseList(Cursor c) {
List<Exercise> result = new ArrayList<>();

View file

@ -21,7 +21,7 @@ public final class ExerciseLocale {
"en", "de"
)
);
};
}
/**
* @return the available language. If the default language of the device is not available. {@code "en"} will be returned.

View file

@ -25,4 +25,4 @@ public class NotificationCancelReceiver extends BroadcastReceiver {
manager.cancel(TimerService.NOTIFICATION_ID);
}
}
};
}

View file

@ -60,7 +60,7 @@ public class TimerSchedulerReceiver extends WakefulBroadcastReceiver {
}
private void startTimer() {
mTimerService.startTimer(mPref.getLong(WORK_TIME, 1000L * 60L * 60L));
mTimerService.startTimer(mPref.getLong(WORK_TIME, 1000L * 60L * 60L), true);
}
public static void scheduleNextAlarm(@NonNull Context context) {

View file

@ -92,6 +92,7 @@ public class TimerService extends Service {
};
private BroadcastReceiver notificationDeletedReceiver = new NotificationDeletedReceiver();
private BroadcastReceiver notificationPreferenceChangedReceiver = new NotificationCancelReceiver();
private boolean scheduled = false;
private void onTimerDone() {
@ -101,7 +102,10 @@ public class TimerService extends Service {
Intent snoozeIntent = new Intent(this, TimerService.class);
snoozeIntent.setAction(ACTION_SNOOZE_TIMER);
PendingIntent startExercises = PendingIntent.getActivity(this, 0, new Intent(this, ExerciseActivity.class), FLAG_CANCEL_CURRENT);
Intent exerciseIntent = new Intent(this, ExerciseActivity.class);
exerciseIntent.putExtra("SCHEDULED", scheduled);
PendingIntent startExercises = PendingIntent.getActivity(this, 0, exerciseIntent, FLAG_CANCEL_CURRENT);
PendingIntent snoozeExercise = PendingIntent.getService(this, 0, snoozeIntent, FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "timer_done");
@ -149,7 +153,17 @@ public class TimerService extends Service {
unregisterReceiver(notificationPreferenceChangedReceiver);
}
public synchronized void startTimer(final long duration) {
public void startTimer(final long duration, boolean scheduled) {
this.scheduled = scheduled;
startTimerInternal(duration);
}
public void startTimer(final long duration) {
this.scheduled = false;
startTimerInternal(duration);
}
private synchronized void startTimerInternal(final long duration) {
if(!isRunning) {
initialDuration = duration;
@ -262,7 +276,7 @@ public class TimerService extends Service {
final String action = intent.getAction();
if (ACTION_START_TIMER.equals(action)) handleRestartTimer();
if (ACTION_START_TIMER.equals(action)) handleRestartTimer(intent);
else if (ACTION_PAUSE_TIMER.equals(action)) pauseTimer();
else if (ACTION_RESUME_TIMER.equals(action)) resumeTimer();
else if (ACTION_STOP_TIMER.equals(action)) stopAndResetTimer();
@ -282,12 +296,19 @@ public class TimerService extends Service {
startTimer(snoozeTime);
}
private void handleRestartTimer(Intent intent) {
if(intent != null) {
scheduled = intent.getBooleanExtra("SCHEDULE", false);
}
handleRestartTimer();
}
private void handleRestartTimer() {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
if(pref.getBoolean(PREF_EXERCISE_CONTINUOUS, false)) {
long duration = pref.getLong(WORK_TIME, 1000 * 60 * 60);
startTimer(duration);
startTimerInternal(duration);
}
}
@ -304,11 +325,15 @@ public class TimerService extends Service {
String time = String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);
PendingIntent startExercises = PendingIntent.getActivity(this, 0, new Intent(this, ExerciseActivity.class), FLAG_CANCEL_CURRENT);
Intent exerciseIntent = new Intent(this, ExerciseActivity.class);
exerciseIntent.putExtra("SCHEDULED", scheduled);
PendingIntent startExercises = PendingIntent.getActivity(this, 0, exerciseIntent, FLAG_CANCEL_CURRENT);
builder.setContentText(time);
builder.setColor(ContextCompat.getColor(this, R.color.colorAccent));
builder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
builder.setOnlyAlertOnce(true);
builder.setSound(null);
builder.setWhen(0);
builder.setProgress((int) initialDuration, (int) (initialDuration - remainingDuration), false);
builder.setSmallIcon(R.mipmap.ic_notification);

View file

@ -291,9 +291,7 @@
android:tint="@color/darkblue"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.33"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:srcCompat="@drawable/ic_replay_black_48dp" />
<ImageButton
@ -308,9 +306,10 @@
android:onClick="onClick"
android:scaleType="fitXY"
android:tint="@color/darkblue"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:srcCompat="@drawable/ic_play_arrow_black" />
</android.support.constraint.ConstraintLayout>

View file

@ -163,5 +163,6 @@
<string name="dialog_warning_not_enough_exercise_time">Die ausgewählte Übungszeit ist zu kurz, um das gewählte Übungsset abzuschließen. Die Übungszeit wird automatisch angepasst. Möchten Sie den Timer starten?</string>
<string name="warning">Achtung</string>
<string name="toast_not_enough_exercise_time">Die gewählte Übungszeit ist nicht ausreichend, um das gewählte Übungsset abzuschließen. Die Zeit wurde automatisch angepasst.</string>
<string name="pref_schedule_random_exercise">Verwende zufällige Übungssets</string>
</resources>

View file

@ -156,6 +156,7 @@
<string name="dialog_warning_not_enough_exercise_time">The chosen exercise time is too short to finish the selected exercise set. The time will automatically be adjusted. Are you sure you want to start the timer?</string>
<string name="warning">Warning</string>
<string name="toast_not_enough_exercise_time">The currently selected exercise time is not sufficient to complete the chosen exercise set. The time has been adjusted.</string>
<string name="pref_schedule_random_exercise">Use random exercise sets</string>
</resources>

View file

@ -35,6 +35,12 @@
android:title="@string/pref_schedule_exercise_days"
android:key="pref_schedule_exercise_days"/>
<SwitchPreference
android:defaultValue="false"
android:dependency="pref_schedule_exercise"
android:title="@string/pref_schedule_random_exercise"
android:key="pref_schedule_random_exercise"/>
</PreferenceCategory>
</PreferenceScreen>