Skip to content

Commit

Permalink
Added spinner to time_dialog to allow the user to opt into permitting…
Browse files Browse the repository at this point in the history
… shake to reset (sleep timer) at any time during the sleep timer.

Addresses issue (AntennaPod#4824)
Addresses issue (AntennaPod#4825)

Details:
	The spinner defaults to "During fadeout" to preserve the original behaviour. If the user opts into the new behaviour by selecting "Anytime" from the spinner, they can shake the reset the sleep timer at any time during the countdown, rather than only during the fadeout. I think this is especially userful for users who do not use the "vibrate" feature of the sleep timer, as the audio fadeout alone can be quite subtle and easily missed.
	This also fixes an issue with the existing sleep timer behaviour - currently the sleep timer will not respect the user unchecking the "shake to reset" option during the fadeout, as the ShakeListener has already been registered, and the callback does not check if the user has enabled shake to reset.
  • Loading branch information
Thom-Merrilin committed Jan 1, 2021
1 parent 3411fcd commit 93fff84
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 17 deletions.
Expand Up @@ -6,6 +6,7 @@
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
Expand Down Expand Up @@ -38,6 +39,8 @@ public class SleepTimerDialog extends DialogFragment {
private LinearLayout timeDisplay;
private TextView time;

private Spinner spShakeBehaviour;

public SleepTimerDialog() {

}
Expand Down Expand Up @@ -115,26 +118,55 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
}, 100);

String[] spinnerContent = new String[] {
String[] spTimeUnitContent = new String[] {
getString(R.string.time_seconds),
getString(R.string.time_minutes),
getString(R.string.time_hours) };
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(),
android.R.layout.simple_spinner_item, spinnerContent);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spTimeUnit.setAdapter(spinnerAdapter);
ArrayAdapter<String> spTimeUnitAdapter = new ArrayAdapter<>(getContext(),
android.R.layout.simple_spinner_item, spTimeUnitContent);
spTimeUnitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spTimeUnit.setAdapter(spTimeUnitAdapter);
spTimeUnit.setSelection(SleepTimerPreferences.lastTimerTimeUnit());

CheckBox cbShakeToReset = content.findViewById(R.id.cbShakeToReset);
spShakeBehaviour = content.findViewById(R.id.spShakeBehaviour);
String[] spShakeBehaviourContent = new String[] {
getString(R.string.shake_during_fadeout), //SHAKE_BEHAVIOUR_FADEOUT = 0
getString(R.string.shake_anytime) }; //SHAKE_BEHAVIOUR_ANYTIME = 1
ArrayAdapter<String> spShakeBehaviourAdapter = new ArrayAdapter<>(getContext(),
android.R.layout.simple_spinner_item, spShakeBehaviourContent);
spShakeBehaviourAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spShakeBehaviour.setAdapter(spShakeBehaviourAdapter);
spShakeBehaviour.setSelection(SleepTimerPreferences.shakeBehaviour());
spShakeBehaviour.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
SleepTimerPreferences.setShakeBehaviour(position);
}

@Override
public void onNothingSelected(AdapterView<?> parent) {
//shouldn't happen
throw new IllegalStateException("Shake Behaviour deselection should not be permitted");
}
});

CheckBox cbVibrate = content.findViewById(R.id.cbVibrate);
CheckBox chAutoEnable = content.findViewById(R.id.chAutoEnable);

cbShakeToReset.setChecked(SleepTimerPreferences.shakeToReset());
boolean shakeToReset = SleepTimerPreferences.shakeToReset();

cbShakeToReset.setChecked(shakeToReset);
cbVibrate.setChecked(SleepTimerPreferences.vibrate());
chAutoEnable.setChecked(SleepTimerPreferences.autoEnable());

setShakeBehaviourVisible(shakeToReset);

cbShakeToReset.setOnCheckedChangeListener((buttonView, isChecked)
-> SleepTimerPreferences.setShakeToReset(isChecked));
-> {
SleepTimerPreferences.setShakeToReset(isChecked);
setShakeBehaviourVisible(isChecked);
});
cbVibrate.setOnCheckedChangeListener((buttonView, isChecked)
-> SleepTimerPreferences.setVibrate(isChecked));
chAutoEnable.setOnCheckedChangeListener((compoundButton, isChecked)
Expand Down Expand Up @@ -176,6 +208,10 @@ private void updateTime() {
time.setText(Converter.getDurationStringLong((int) controller.getSleepTimerTimeLeft()));
}

private void setShakeBehaviourVisible(boolean visible) {
spShakeBehaviour.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}

private void closeKeyboard(View content) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
Expand Down
20 changes: 18 additions & 2 deletions app/src/main/res/layout/time_dialog.xml
Expand Up @@ -114,12 +114,28 @@
android:orientation="vertical"
android:layout_marginTop="8dp">

<CheckBox
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<CheckBox
android:id="@+id/cbShakeToReset"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:text="@string/shake_to_reset_label"/>

<Spinner
android:id="@+id/spShakeBehaviour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"/>

</RelativeLayout>

<CheckBox
android:id="@+id/cbVibrate"
android:layout_width="match_parent"
Expand Down
Expand Up @@ -16,13 +16,19 @@ public class SleepTimerPreferences {
private static final String PREF_TIME_UNIT = "LastTimeUnit";
private static final String PREF_VIBRATE = "Vibrate";
private static final String PREF_SHAKE_TO_RESET = "ShakeToReset";
private static final String PREF_SHAKE_BEHAVIOUR = "lastShakeBehaviour";
private static final String PREF_AUTO_ENABLE = "AutoEnable";

private static final TimeUnit[] UNITS = { TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS };

private static final String DEFAULT_VALUE = "15";
private static final int DEFAULT_TIME_UNIT = 1;

private static final int SHAKE_BEHAVIOUR_FADEOUT = 0;
private static final int SHAKE_BEHAVIOUR_ANYTIME = 1;

private static final int DEFAULT_SHAKE_BEHAVIOUR = SHAKE_BEHAVIOUR_FADEOUT;

private static SharedPreferences prefs;

/**
Expand Down Expand Up @@ -64,10 +70,22 @@ public static void setShakeToReset(boolean shakeToReset) {
prefs.edit().putBoolean(PREF_SHAKE_TO_RESET, shakeToReset).apply();
}

public static void setShakeBehaviour(int shakeBehaviour) {
prefs.edit().putInt(PREF_SHAKE_BEHAVIOUR, shakeBehaviour).apply();
}

public static boolean shakeToReset() {
return prefs.getBoolean(PREF_SHAKE_TO_RESET, true);
}

public static int shakeBehaviour() {
return prefs.getInt(PREF_SHAKE_BEHAVIOUR, DEFAULT_SHAKE_BEHAVIOUR);
}

public static boolean allowShake(boolean almostExpired) {
return shakeToReset() && (almostExpired || (shakeBehaviour() == SHAKE_BEHAVIOUR_ANYTIME));
}

public static void setAutoEnable(boolean autoEnable) {
prefs.edit().putBoolean(PREF_AUTO_ENABLE, autoEnable).apply();
}
Expand Down
Expand Up @@ -401,7 +401,15 @@ public void run() {
timeLeft -= now - lastTick;
lastTick = now;

if (timeLeft < NOTIFICATION_THRESHOLD) {
boolean almostExpired = timeLeft < NOTIFICATION_THRESHOLD;

if (shakeListener == null && SleepTimerPreferences.allowShake(almostExpired)) {
shakeListener = new ShakeListener(context, this);
} else if (!SleepTimerPreferences.allowShake(almostExpired)) {
removeShakeListener();
}

if (almostExpired) {
Log.d(TAG, "Sleep timer is about to expire");
if (SleepTimerPreferences.vibrate() && !hasVibrated) {
Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Expand All @@ -410,17 +418,11 @@ public void run() {
hasVibrated = true;
}
}
if (shakeListener == null && SleepTimerPreferences.shakeToReset()) {
shakeListener = new ShakeListener(context, this);
}
postCallback(() -> callback.onSleepTimerAlmostExpired(timeLeft));
}
if (timeLeft <= 0) {
Log.d(TAG, "Sleep timer expired");
if (shakeListener != null) {
shakeListener.pause();
shakeListener = null;
}
removeShakeListener();
hasVibrated = false;
if (!Thread.currentThread().isInterrupted()) {
postCallback(callback::onSleepTimerExpired);
Expand All @@ -431,6 +433,13 @@ public void run() {
}
}

private void removeShakeListener() {
if (shakeListener != null) {
shakeListener.pause();
shakeListener = null;
}
}

public long getWaitingTime() {
return timeLeft;
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/res/values/strings.xml
Expand Up @@ -606,6 +606,8 @@
<string name="sleep_timer_label">Sleep timer</string>
<string name="time_dialog_invalid_input">Invalid input, time has to be an integer</string>
<string name="shake_to_reset_label">Shake to reset</string>
<string name="shake_during_fadeout">During fadeout</string>
<string name="shake_anytime">Anytime</string>
<string name="timer_vibration_label">Vibrate shortly before end</string>
<string name="time_seconds">seconds</string>
<string name="time_minutes">minutes</string>
Expand Down

0 comments on commit 93fff84

Please sign in to comment.