/*
* Copyright (C) 2013 jonas.oreland@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.runnerup.widget;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.TypedArray;
import android.os.Build;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import android.widget.TimePicker;
import org.runnerup.R;
import org.runnerup.util.SafeParse;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@TargetApi(Build.VERSION_CODES.FROYO)
public class TitleSpinner extends LinearLayout {
private enum Type {
TS_SPINNER,
TS_SPINNER_TXT,
TS_EDITTEXT,
TS_DATEPICKER,
TS_TIMEPICKER,
TS_DURATIONPICKER,
TS_DISTANCEPICKER,
TS_NUMBERPICKER
}
private String mKey = null;
private TextView mTitle = null;
private TextView mValue = null;
private Spinner mSpinner = null;
int mInputType = 0;
private final Context mContext;
private OnSetValueListener mSetValueListener = null;
private OnCloseDialogListener mCloseDialogListener = null;
private Type mType = null;
private boolean mFirstSetValue = true;
private int values[] = null;
private long mCurrValue = -1;
public interface OnSetValueListener {
/**
* @param newValue
* @return
* @throws java.lang.IllegalArgumentException
*/
public String preSetValue(String newValue) throws java.lang.IllegalArgumentException;
/**
* @param newValue
* @return
* @throws java.lang.IllegalArgumentException
*/
public int preSetValue(int newValue) throws java.lang.IllegalArgumentException;
}
public interface OnCloseDialogListener {
public void onClose(TitleSpinner spinner, boolean ok);
}
public TitleSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.title_spinner, this);
mTitle = (TextView) findViewById(R.id.title);
mValue = (TextView) findViewById(R.id.value);
mSpinner = (Spinner) findViewById(R.id.spinner);
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.TitleSpinner);
CharSequence title = arr.getString(R.styleable.TitleSpinner_android_text);
if (title != null) {
mTitle.setText(title);
}
//Note: R.styleable.TitleSpinner_android_prompt is not used
CharSequence type = arr.getString(R.styleable.TitleSpinner_type);
CharSequence defaultValue = arr.getString(R.styleable.TitleSpinner_android_defaultValue);
if (type == null || "spinner".contentEquals(type)) {
mType = Type.TS_SPINNER;
setupSpinner(context, attrs, arr, defaultValue);
} else if ("spinner_txt".contentEquals(type)) {
mType = Type.TS_SPINNER_TXT;
setupSpinner(context, attrs, arr, defaultValue);
} else if ("edittext".contentEquals(type)) {
mType = Type.TS_EDITTEXT;
setupEditText(context, attrs, arr, defaultValue);
} else if ("datepicker".contentEquals(type)) {
mType = Type.TS_DATEPICKER;
setupDatePicker(context, attrs, arr, defaultValue);
} else if ("timepicker".contentEquals(type)) {
mType = Type.TS_TIMEPICKER;
setupTimePicker(context, attrs, arr, defaultValue);
} else if ("durationpicker".contentEquals(type)) {
mType = Type.TS_DURATIONPICKER;
setupDurationPicker(context, attrs, arr, defaultValue);
} else if ("distancepicker".contentEquals(type)) {
mType = Type.TS_DISTANCEPICKER;
setupDistancePicker(context, attrs, arr, defaultValue);
} else if ("numberpicker".contentEquals(type)) {
mType = Type.TS_NUMBERPICKER;
setupNumberPicker(context, attrs, arr, defaultValue);
} else {
arr = null; // force null pointer exception
}
CharSequence key = arr.getString(R.styleable.TitleSpinner_android_key);
if (key != null) {
mKey = key.toString();
loadValue(defaultValue != null ? defaultValue.toString() : null);
}
arr.recycle(); // Do this when done.
}
private void setupEditText(final Context context, final AttributeSet attrs, TypedArray arr, CharSequence defaultValue) {
mInputType = arr.getInt(R.styleable.TitleSpinner_android_inputType,
EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_FLAG_DECIMAL);
setValue(defaultValue, false);
final EditText edit = new EditText(context, attrs);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(mTitle.getText());
edit.setText(mValue.getText());
edit.setInputType(mInputType);
final LinearLayout layout = createLayout(context);
layout.addView(edit);
alert.setView(layout);
alert.setPositiveButton(getResources().getString(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setValue(edit.getText().toString());
dialog.dismiss();
layout.removeView(edit);
onClose(true);
}
});
alert.setNegativeButton(getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
layout.removeView(edit);
onClose(false);
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
});
}
private void setupSpinner(Context context, AttributeSet attrs, TypedArray arr, CharSequence defaultValue) {
mSpinner.setPrompt(mTitle.getText());
int entriesId = arr.getResourceId(R.styleable.TitleSpinner_android_entries, 0);
int valuesId = arr.getResourceId(R.styleable.TitleSpinner_values, 0);
if (valuesId != 0) {
values = getResources().getIntArray(valuesId);
}
if (entriesId != 0) {
DisabledEntriesAdapter adapter = new DisabledEntriesAdapter(mContext, entriesId);
mSpinner.setAdapter(adapter);
int value = 0;
if (defaultValue != null) {
value = SafeParse.parseInt(defaultValue.toString(), 0);
}
setValue(value);
// if (value >= 0 && value < entries.length) {
// mValueInt = value;
// mValue.setText(entries[value]);
// }
}
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSpinner.performClick();
}
});
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
if (mType == Type.TS_SPINNER_TXT) {
if (mSpinner.getAdapter() != null) {
setValue(mSpinner.getAdapter().getItem(arg2).toString());
}
} else {
setValue(getRealValue(arg2));
}
if (!mFirstSetValue) {
onClose(true);
}
mFirstSetValue = false;
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
}
private static LinearLayout createLayout(Context context) {
final LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
layout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
return layout;
}
private void setupDatePicker(final Context context, AttributeSet attrs, TypedArray arr, CharSequence defaultValue) {
if (defaultValue != null && "today".contentEquals(defaultValue)) {
DateFormat df = android.text.format.DateFormat.getDateFormat(context);
defaultValue = df.format(new Date());
}
setValue(defaultValue, false);
final DatePicker datePicker = new DatePicker(context, attrs);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(mTitle.getText());
final LinearLayout layout = createLayout(context);
layout.addView(datePicker);
alert.setView(layout);
alert.setPositiveButton(getResources().getString(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setValue(getValue(datePicker));
dialog.dismiss();
layout.removeView(datePicker);
onClose(true);
}
private String getValue(DatePicker dp) {
Calendar c = Calendar.getInstance();
c.set(dp.getYear(), dp.getMonth(), dp.getDayOfMonth());
DateFormat df = android.text.format.DateFormat.getDateFormat(context);
return df.format(c.getTime());
}
});
alert.setNegativeButton(getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
layout.removeView(datePicker);
onClose(false);
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
});
}
private void setupTimePicker(final Context context, AttributeSet attrs, TypedArray arr, CharSequence defaultValue) {
if (defaultValue != null && "now".contentEquals(defaultValue)) {
DateFormat df = android.text.format.DateFormat.getTimeFormat(context);
defaultValue = df.format(new Date());
}
setValue(defaultValue, false);
final TimePicker timePicker = new TimePicker(context, attrs);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(mTitle.getText());
timePicker.setIs24HourView(true);
final LinearLayout layout = createLayout(context);
layout.addView(timePicker);
alert.setView(layout);
alert.setPositiveButton(getResources().getString(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setValue(getValue(timePicker));
dialog.dismiss();
layout.removeView(timePicker);
onClose(true);
}
private String getValue(TimePicker dp) {
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR, dp.getCurrentHour());
c.set(Calendar.MINUTE, dp.getCurrentMinute());
DateFormat df = android.text.format.DateFormat.getTimeFormat(context);
return df.format(c.getTime());
}
});
alert.setNegativeButton(getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
layout.removeView(timePicker);
onClose(false);
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
});
}
private void setupDurationPicker(final Context context, final AttributeSet attrs, TypedArray arr,
CharSequence defaultValue) {
setValue(defaultValue, false);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(mTitle.getText());
final DurationPicker picker = new DurationPicker(context, attrs);
picker.setEpochTime(mCurrValue);
final LinearLayout layout = createLayout(context);
layout.addView(picker);
alert.setView(layout);
alert.setPositiveButton(getResources().getString(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setValue(getValue(picker));
dialog.dismiss();
layout.removeView(picker);
onClose(true);
}
private String getValue(DurationPicker dp) {
return DateUtils.formatElapsedTime(picker.getEpochTime());
}
});
alert.setNegativeButton(getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
layout.removeView(picker);
onClose(false);
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
});
}
private void setupDistancePicker(final Context context, AttributeSet attrs, TypedArray arr,
CharSequence defaultValue) {
setValue(defaultValue, false);
final DistancePicker distancePicker = new DistancePicker(context, attrs);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(mTitle.getText());
distancePicker.setDistance(mCurrValue);
final LinearLayout layout = createLayout(context);
layout.addView(distancePicker);
alert.setView(layout);
alert.setPositiveButton(getResources().getString(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setValue(getValue(distancePicker));
dialog.dismiss();
layout.removeView(distancePicker);
onClose(true);
}
private String getValue(DistancePicker dp) {
return Long.toString(dp.getDistance());
}
});
alert.setNegativeButton(getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
layout.removeView(distancePicker);
onClose(false);
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
});
}
private void setupNumberPicker(final Context context, AttributeSet attrs, final TypedArray arr, CharSequence defaultValue) {
setValue(defaultValue, false);
final NumberPicker numberPicker = new NumberPicker(context, attrs);
numberPicker.setOrientation(VERTICAL);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(mTitle.getText());
numberPicker.setValue((int)mCurrValue);
final LinearLayout layout = createLayout(context);
layout.addView(numberPicker);
alert.setView(layout);
alert.setPositiveButton(getResources().getString(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setValue(getValue(numberPicker));
dialog.dismiss();
layout.removeView(numberPicker);
onClose(true);
}
private String getValue(NumberPicker dp) {
return Integer.toString(dp.getValue());
}
});
alert.setNegativeButton(getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
layout.removeView(numberPicker);
onClose(false);
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
});
}
public void setAdapter(SpinnerAdapter adapter) {
mSpinner.setAdapter(adapter);
loadValue(null);
}
public void setOnSetValueListener(OnSetValueListener listener) {
this.mSetValueListener = listener;
}
public void setOnCloseDialogListener(OnCloseDialogListener listener) {
this.mCloseDialogListener = listener;
}
private void onClose(boolean b) {
if (mCloseDialogListener != null)
mCloseDialogListener.onClose(this, b);
}
public void setTitle(String title) {
mTitle.setText(title);
}
private void loadValue(String defaultValue) {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mContext);
if (pref == null)
return;
switch (mType) {
case TS_SPINNER:
int def = 0;
if (defaultValue != null) {
def = SafeParse.parseInt(defaultValue, 0);
}
setValue(pref.getInt(mKey, def));
break;
case TS_SPINNER_TXT:
case TS_EDITTEXT:
case TS_DURATIONPICKER:
case TS_DISTANCEPICKER:
case TS_NUMBERPICKER:
case TS_DATEPICKER:
case TS_TIMEPICKER:
final String val = pref.getString(mKey, defaultValue == null ? "" : defaultValue);
setValue(val);
break;
}
}
public void setValue(String value) {
setValue (value, true);
}
public void setValue(CharSequence value, Boolean savePreferences) {
String str = value == null ? "" : value.toString();
setValue(str, savePreferences);
}
public void setValue(String value, Boolean savePreferences) {
if (mSetValueListener != null) {
try {
value = mSetValueListener.preSetValue(value);
} catch (java.lang.IllegalArgumentException ex) {
return;
}
}
//Store the value - could be separate for distance vs time
if (value == null) {
mCurrValue = 0;
} else if (mType == Type.TS_DURATIONPICKER) {
mCurrValue = SafeParse.parseSeconds(value, 0);
} else {
mCurrValue = (long) SafeParse.parseDouble(value.toString(), 0);
}
if (mType == Type.TS_DISTANCEPICKER && !TextUtils.isEmpty(value)) {
mValue.setText(String.format("%s %s", value, getResources().getString(R.string.metrics_distance_m)));
} else {
mValue.setText(value);
}
if (mType == Type.TS_SPINNER_TXT) {
if (mSpinner.getAdapter() != null) {
int intVal = find(mSpinner.getAdapter(), value);
mSpinner.setSelection(intVal);
}
}
if (mKey == null || !savePreferences)
return;
Editor pref = PreferenceManager.getDefaultSharedPreferences(mContext).edit();
pref.putString(mKey, value);
pref.commit();
}
private int find(SpinnerAdapter adapter, String value) {
for (int i = 0; i < adapter.getCount(); i++) {
if (value.contentEquals(adapter.getItem(i).toString())) {
return i;
}
}
return 0;
}
int getSelectionValue(int value) {
if (values == null)
return value;
int p = 0;
for (int v : values) {
if (v == value)
return p;
p++;
}
/* not found, hmm...what to do... */
return 0;
}
int getRealValue(int value) {
if (values == null)
return value;
if (value >= 0 && value < values.length)
return values[value];
/* invalid value, hmm...what to do... */
return values[0];
}
public void setValue(int value) {
if (mSetValueListener != null) {
try {
value = mSetValueListener.preSetValue(value);
} catch (java.lang.IllegalArgumentException ex) {
if ((int)mCurrValue != -1) {
mSpinner.setSelection((int)mCurrValue);
}
return;
}
}
mCurrValue = value;
int selectionValue = getSelectionValue(value);
mSpinner.setSelection(selectionValue);
if (mSpinner.getAdapter() != null) {
Object val = mSpinner.getAdapter().getItem(selectionValue);
if (val != null)
mValue.setText(val.toString());
else
mValue.setText("");
}
if (mKey == null)
return;
Editor pref = PreferenceManager.getDefaultSharedPreferences(mContext).edit();
pref.putInt(mKey, value);
pref.commit();
}
public void addDisabledValue(int value) {
int selection = getSelectionValue(value);
((DisabledEntriesAdapter)mSpinner.getAdapter()).addDisabled(selection);
}
public void clearDisabled() {
((DisabledEntriesAdapter)mSpinner.getAdapter()).clearDisabled();
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
LinearLayout layout = (LinearLayout) findViewById(R.id.title_spinner);
layout.setEnabled(enabled);
mSpinner.setEnabled(enabled);
}
public CharSequence getValue() {
switch(mType) {
case TS_SPINNER_TXT:
case TS_EDITTEXT:
return mValue.getText();
case TS_DATEPICKER:
case TS_TIMEPICKER:
case TS_DURATIONPICKER:
case TS_DISTANCEPICKER:
case TS_NUMBERPICKER:
case TS_SPINNER:
break;
}
return String.format(Locale.getDefault(), "%d", mCurrValue);
}
public int getValueInt() {
return (int)mCurrValue;
}
public void clear() {
if (mKey != null) {
PreferenceManager.getDefaultSharedPreferences(mContext).edit().remove(mKey).commit();
}
}
}