Commit e179b93f authored by Trevor Slocum's avatar Trevor Slocum

Add custom vibration patterns

Resolves #28
parent 2798f077
......@@ -2,6 +2,7 @@
- Add resources menu linking to how to meditate, /r/meditation, an overview of MediNET and replaying the tutorial
- Add presets to tutorial
- Add session volume preview
- Add custom vibration patterns
- Resolve possible crash when waking device
- Resolve duration not being preselected when editing
- Remove usage of Google Play Services (was previously used in releases on Google Play and Amazon)
......
......@@ -146,8 +146,8 @@ public class CompleteActivity extends Activity {
}
String finishSoundPath = getMeditationAssistant().getPrefs().getString("pref_meditation_sound_finish", "");
if (!manual && !finishSoundPath.equals("none")) {
getMeditationAssistant().playSessionSound(2, false);
if (!manual) {
getMeditationAssistant().notifySession(2, false, false);
} else {
getMeditationAssistant().restoreVolume();
}
......@@ -156,8 +156,6 @@ public class CompleteActivity extends Activity {
}
if (!manual) {
getMeditationAssistant().vibrateDevice();
String autosave = getMeditationAssistant().getPrefs().getString("pref_autosave", "");
if (autosave.equals("save")) {
saveMediNET(null);
......
......@@ -43,8 +43,6 @@ public class ListPreferenceSound extends ListPreference {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ListPreference, 0, 0);
// mEntries = a.getTextArray(R.styleable.ListPreference_entries);
// mEntryValues = a.getTextArray(R.styleable.ListPreference_entryValues);
setEntries(a.getTextArray(R.styleable.ListPreference_entries));
setEntryValues(a.getTextArray(R.styleable.ListPreference_entryValues));
......@@ -99,7 +97,6 @@ public class ListPreferenceSound extends ListPreference {
if (mSummary == null || entry == null) {
return super.getSummary();
} else {
//Log.d("MeditationAssistant", "getsummary(): " + String.valueOf(mSummary) + " " + String.valueOf(entry));
try {
return String.format(mSummary.toString(), entry);
} catch (Exception e) {
......@@ -152,8 +149,7 @@ public class ListPreferenceSound extends ListPreference {
e.printStackTrace();
}
}
if (positiveResult && mClickedDialogEntryIndex >= 0
&& mEntryValues != null) {
if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
String value = mEntryValues[mClickedDialogEntryIndex].toString();
if (callChangeListener(value)) {
setValue(value);
......@@ -262,10 +258,8 @@ public class ListPreferenceSound extends ListPreference {
builder.setPositiveButton(
builder.getContext().getString(R.string.set),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("MeditationAssistant", "Set clicked");
ListPreferenceSound.this.onClick(dialog,
DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
......
package sh.ftp.rocketninelabs.meditationassistant;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.ListPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
public class ListPreferenceVibration extends ListPreference {
private int mClickedDialogEntryIndex;
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
private CharSequence mSummary;
private String mValue;
private Context ctx = null;
private SharedPreferences prefs = null;
public ListPreferenceVibration(Context context) {
this(context, null);
}
public ListPreferenceVibration(Context context, AttributeSet attrs) {
super(context, attrs);
ctx = context;
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ListPreference, 0, 0);
setEntries(a.getTextArray(R.styleable.ListPreference_entries));
setEntryValues(a.getTextArray(R.styleable.ListPreference_entryValues));
a.recycle();
mSummary = super.getSummary();
}
public int findIndexOfValue(String value) {
if (value != null && mEntryValues != null) {
for (int i = mEntryValues.length - 1; i >= 0; i--) {
if (mEntryValues[i].equals(value)) {
return i;
}
}
}
return -1;
}
public CharSequence[] getEntries() {
return mEntries;
}
public void setEntries(CharSequence[] entries) {
mEntries = entries;
}
public void setEntries(int entriesResId) {
setEntries(getContext().getResources().getTextArray(entriesResId));
}
public CharSequence getEntry() {
int index = getValueIndex();
return index >= 0 && mEntries != null ? mEntries[index] : null;
}
public CharSequence[] getEntryValues() {
return mEntryValues;
}
public void setEntryValues(CharSequence[] entryValues) {
mEntryValues = entryValues;
}
public void setEntryValues(int entryValuesResId) {
setEntryValues(getContext().getResources().getTextArray(
entryValuesResId));
}
@Override
public CharSequence getSummary() {
final CharSequence entry = getEntry();
if (mSummary == null || entry == null) {
return super.getSummary();
} else {
try {
return String.format(mSummary.toString(), entry);
} catch (Exception e) {
e.printStackTrace();
return mSummary.toString();
}
}
}
@Override
public void setSummary(CharSequence summary) {
super.setSummary(summary);
if (summary == null && mSummary != null) {
mSummary = null;
} else if (summary != null && !summary.equals(mSummary)) {
mSummary = summary;
}
}
public String getValue() {
return mValue;
}
public void setValue(String value) {
mValue = value;
persistString(value);
}
private int getValueIndex() {
return findIndexOfValue(mValue);
}
public void setValueIndex(int index) {
if (mEntryValues != null) {
setValue(mEntryValues[index].toString());
}
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
String value = mEntryValues[mClickedDialogEntryIndex].toString();
if (callChangeListener(value)) {
setValue(value);
}
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
if (mEntries == null || mEntryValues == null) {
throw new IllegalStateException(
"ListPreference requires an entries array and an entryValues array.");
}
mClickedDialogEntryIndex = getValueIndex();
builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mClickedDialogEntryIndex = which;
String itemSelected = mEntryValues[mClickedDialogEntryIndex]
.toString();
Log.d("MeditationAssistant",
"Selected: " + which + " - "
+ itemSelected
);
if (itemSelected.equals("custom")) {
ListPreferenceVibration.this.onClick(dialog,
DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
} else if (!itemSelected.equals("")) {
((MeditationAssistant) ctx.getApplicationContext()).vibrateDevice(itemSelected);
}
}
}
);
builder.setPositiveButton(
builder.getContext().getString(R.string.set),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ListPreferenceVibration.this.onClick(dialog,
DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
}
}
);
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setValue(myState.value);
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
return superState;
}
final SavedState myState = new SavedState(superState);
myState.value = getValue();
return myState;
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
mValue = restoreValue ? getPersistedString(mValue)
: (String) defaultValue;
}
private static class SavedState extends BaseSavedState {
String value;
public SavedState(Parcel source) {
super(source);
value = source.readString();
}
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(value);
}
}
public SharedPreferences getPrefs() {
if (prefs == null) {
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
}
return prefs;
}
}
......@@ -9,12 +9,18 @@ public class Preset {
public String delay = "";
public String startsound = "";
public String startsoundcustom = "";
public String startvibration = "";
public String startvibrationcustom = "";
public String intervalduration = "";
public String intervalsound = "";
public String intervalsoundcustom = "";
public String intervalvibration = "";
public String intervalvibrationcustom = "";
public String intervalcount = "";
public String completesound = "";
public String completesoundcustom = "";
public String completevibration = "";
public String completevibrationcustom = "";
public String ringtone = "";
public Integer volume = 50;
public Boolean endless = false;
......@@ -28,12 +34,18 @@ public class Preset {
jobj.put("delay", delay);
jobj.put("startsound", startsound);
jobj.put("startsoundcustom", startsoundcustom);
jobj.put("startvibration", startvibration);
jobj.put("startvibrationcustom", startvibrationcustom);
jobj.put("intervalduration", intervalduration);
jobj.put("intervalsound", intervalsound);
jobj.put("intervalsoundcustom", intervalsoundcustom);
jobj.put("intervalvibration", intervalvibration);
jobj.put("intervalvibrationcustom", intervalvibrationcustom);
jobj.put("intervalcount", intervalcount);
jobj.put("completesound", completesound);
jobj.put("completesoundcustom", completesoundcustom);
jobj.put("completevibration", completevibration);
jobj.put("completevibrationcustom", completevibrationcustom);
jobj.put("ringtone", ringtone);
jobj.put("volume", volume);
jobj.put("endless", endless);
......
......@@ -134,13 +134,7 @@ public class ProgressActivity extends FragmentActivity {
}
sessionDetailsDialog = new AlertDialog.Builder(this)
.setIcon(
getResources().getDrawable(
getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true),
new int[]{R.attr.actionIconGoToToday})
.getResourceId(0, 0)
)
)
.setIcon(getResources().getDrawable(getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true), new int[]{R.attr.actionIconGoToToday}).getResourceId(0, 0)))
.setTitle(sdf.format(sess_date))
.setAdapter(sessionsDialogAdapter,
new DialogInterface.OnClickListener() {
......@@ -206,13 +200,7 @@ public class ProgressActivity extends FragmentActivity {
}
sessionDetailsDialog = new AlertDialog.Builder(this)
.setIcon(
getResources().getDrawable(
getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true),
new int[]{R.attr.actionIconGoToToday})
.getResourceId(0, 0)
)
)
.setIcon(getResources().getDrawable(getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true), new int[]{R.attr.actionIconGoToToday}).getResourceId(0, 0)))
.setTitle(session_title)
.setView(detailsView)
.create();
......
......@@ -59,13 +59,7 @@ public class SessionsFragment extends ListFragment {
selected_session = (SessionSQL) getListView().getItemAtPosition(position);
sessionDialog = new AlertDialog.Builder(getActivity())
.setIcon(
getActivity().getResources().getDrawable(
getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true),
new int[]{R.attr.actionIconGoToToday})
.getResourceId(0, 0)
)
)
.setIcon(getActivity().getResources().getDrawable(getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true), new int[]{R.attr.actionIconGoToToday}).getResourceId(0, 0)))
.setTitle(session_title)
.setItems(R.array.session_actions,
new DialogInterface.OnClickListener() {
......@@ -94,15 +88,8 @@ public class SessionsFragment extends ListFragment {
getMeditationAssistant().getMediNET().postSession(0, null, null);
}
} else { // Delete
AlertDialog deleteDialog = new AlertDialog.Builder(
getActivity())
.setIcon(
getActivity().getResources().getDrawable(
getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true),
new int[]{R.attr.actionIconGoToToday})
.getResourceId(0, 0)
)
)
AlertDialog deleteDialog = new AlertDialog.Builder(getActivity())
.setIcon(getActivity().getResources().getDrawable(getMeditationAssistant().getTheme().obtainStyledAttributes(getMeditationAssistant().getMATheme(true), new int[]{R.attr.actionIconGoToToday}).getResourceId(0, 0)))
.setTitle(session_title)
.setItems(
R.array.session_delete_actions,
......@@ -137,15 +124,16 @@ public class SessionsFragment extends ListFragment {
}
}
}
).create();
)
.create();
deleteDialog.show();
}
}
}
).create();
)
.create();
sessionDialog.show();
return true;
}
});
......
......@@ -68,8 +68,8 @@ class WakeLocker {
void releaseAll() {
Log.d("MeditationAssistant", "WAKELOCKER: Releasing all wakelocks");
for (String wakeLockID : wakeLocks) {
List<String> wakeLocksCopy = new ArrayList<>(wakeLocks);
for (String wakeLockID : wakeLocksCopy) {
release(wakeLockID);
}
}
......
<!--suppress AndroidMissingOnClickHandler -->
<RelativeLayout
android:id="@+id/medinetsignin_root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="7dp"
android:paddingLeft="7dp"
android:paddingRight="7dp"
tools:context=".MediNET">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="@string/pattern"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<EditText
android:id="@+id/editVibrationPattern"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="14dp"
android:singleLine="true"
android:text=""
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
android:includeFontPadding="false"
android:text="@string/setVibrationTip"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="13sp"/>
</LinearLayout>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="empty_array">
</string-array>
<string-array name="empty_array"></string-array>
<string-array name="session_actions">
<item>@string/post</item>
<item>@string/delete</item>
......@@ -127,10 +126,13 @@
<item>introduction</item>
<item>delay</item>
<item>startsound</item>
<item>startvibration</item>
<item>intervalduration</item>
<item>intervalsound</item>
<item>intervalvibration</item>
<item>intervalcount</item>
<item>completesound</item>
<item>completevibration</item>
<item>ringtone</item>
<item>volume</item>
<item>endless</item>
......@@ -141,14 +143,16 @@
<item>@string/pref_sessionintro</item>
<item>@string/pref_delay</item>
<item>@string/pref_meditation_sound_start</item>
<item>@string/pref_meditation_vibrate_start</item>
<item>@string/pref_interval</item>
<item>@string/pref_meditation_sound_interval</item>
<item>@string/pref_meditation_vibrate_interval</item>
<item>@string/pref_interval_count</item>
<item>@string/pref_meditation_sound_finish</item>
<item>@string/pref_meditation_vibrate_finish</item>
<item>@string/pref_notificationcontrol</item>
<item>@string/pref_sessionvolume</item>
<item>@string/pref_softfinish</item>
<item>@string/pref_vibrate</item>
</string-array>
<string-array name="presetsettings_default">
<item>modeandduration</item>
......@@ -163,5 +167,19 @@
<item>save</item>
<item>post</item>
</string-array>
<string-array name="vibration_values">
<item></item>
<item>short</item>
<item>medium</item>
<item>long</item>
<item>custom</item>
</string-array>
<string-array name="vibration">
<item>@string/disabled</item>
<item>@string/labelShort</item>
<item>@string/labelMedium</item>
<item>@string/labelLong</item>
<item>@string/custom</item>
</string-array>
</resources>
\ No newline at end of file
......@@ -52,8 +52,10 @@
<string name="setPresetHint">Hold to set preset</string>
<string name="setPresetTip">Choose which settings to restore by tapping the gear icon (Settings), then tap Preset settings.</string>
<string name="setPresetHintBlank">Enter a duration (or end time) to set preset</string>
<string name="setVibrationTip">Enter the number of milliseconds to turn the vibrator on or off, separated by commas.</string>
<string name="configureWidget">Configure Widget</string>
<string name="label">Label</string>
<string name="pattern">Pattern</string>
<string name="setTimerDurationHint">Enter a valid duration (e.g. 0:22)</string>
<string name="setTimerEndAtHint">Enter a valid time (e.g. 4:20)</string>
<string name="invalidEndAt">Invalid time to end session</string>
......@@ -148,7 +150,10 @@
<string name="reminderText">Do you have a moment?</string>
<!-- Settings -->
<string name="meditation">Meditation</string>
<string name="pref_vibrate">Vibrate</string>
<string name="vibrate">Vibrate</string>
<string name="pref_meditation_vibrate_start">Start vibration</string>
<string name="pref_meditation_vibrate_interval">Interval vibration</string>
<string name="pref_meditation_vibrate_finish">Complete vibration</string>
<string name="pref_vibrate_summary">Occurs at the beginning, intervals, and end of meditation</string>
<string name="pref_vibrate_reminder_summary">The daily reminder will vibrate the device</string>
<string name="pref_sound_reminder">Play sound</string>
......@@ -159,6 +164,10 @@
<string name="disabled">Disabled</string>
<string name="none">None</string>
<string name="noSpecialBehavior">No special behavior</string>
<string name="labelShort">Short</string>
<string name="labelMedium">Medium</string>
<string name="labelLong">Long</string>
<string name="custom">Custom</string>
<string name="noSound">No sound</string>
<string name="customSound">Custom sound</string>
<string name="duringSession">During session</string>
......
......@@ -19,7 +19,7 @@
android:dependency="pref_daily_reminder"
android:key="pref_vibrate_reminder"
android:summary="@string/pref_vibrate_reminder_summary"
android:title="@string/pref_vibrate"/>
android:title="@string/vibrate"/>
<EditTextPreference
android:defaultValue=""
......
......@@ -32,6 +32,15 @@
android:key="pref_meditation_sound_start"
android:title="@string/pref_meditation_sound_start"/>
<sh.ftp.rocketninelabs.meditationassistant.ListPreferenceVibration
android:defaultValue=""
android:dialogIcon="?attr/actionIconFlashOn"
android:entries="@array/vibration"
android:entryValues="@array/vibration_values"
android:icon="?attr/actionIconFlashOn"
android:key="pref_meditation_vibrate_start"