Create new time picker dialog each time as needed

This commit is contained in:
Phillip Hsu 2016-07-15 17:12:25 -07:00
parent 0f32215145
commit c3b018f59e
4 changed files with 126 additions and 23 deletions

View File

@ -4,6 +4,7 @@ import android.content.Intent;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.CoordinatorLayout;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
@ -73,6 +74,13 @@ public class EditAlarmActivity extends BaseActivity implements
private static final RelativeSizeSpan AMPM_SIZE_SPAN = new RelativeSizeSpan(0.5f); private static final RelativeSizeSpan AMPM_SIZE_SPAN = new RelativeSizeSpan(0.5f);
private static final String TAG_TIME_PICKER = "time_picker"; private static final String TAG_TIME_PICKER = "time_picker";
private static final String KEY_INPUT_TIME = "input_time";
private static final String KEY_ENABLED = "enabled";
private static final String KEY_CHECKED_DAYS = "checked_days";
private static final String KEY_LABEL = "label";
private static final String KEY_RINGTONE_URI = "ringtone";
private static final String KEY_VIBRATE = "vibrate";
private static final int REQUEST_PICK_RINGTONE = 0; private static final int REQUEST_PICK_RINGTONE = 0;
private static final int ID_MENU_ITEM = 0; private static final int ID_MENU_ITEM = 0;
@ -82,6 +90,12 @@ public class EditAlarmActivity extends BaseActivity implements
private int mSelectedHourOfDay = -1; private int mSelectedHourOfDay = -1;
private int mSelctedMinute = -1; private int mSelctedMinute = -1;
// If we keep a reference to the dialog, we keep its previous state as well.
// So the next time we call show() on this, the input field will show the
// last inputted time. The easiest workaround is to always create a new
// instance each time we want to show the dialog.
// private NumpadTimePickerDialog mPicker;
@Bind(R.id.main_content) CoordinatorLayout mMainContent; @Bind(R.id.main_content) CoordinatorLayout mMainContent;
@Bind(R.id.save) Button mSave; @Bind(R.id.save) Button mSave;
@Bind(R.id.delete) Button mDelete; @Bind(R.id.delete) Button mDelete;
@ -105,7 +119,20 @@ public class EditAlarmActivity extends BaseActivity implements
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setWeekDaysText(); setWeekDaysText();
// Are we recreating this Activity because of a rotation? If so, try finding
// the time picker in our backstack.
NumpadTimePickerDialog picker = (NumpadTimePickerDialog)
getSupportFragmentManager().findFragmentByTag(TAG_TIME_PICKER);
if (picker != null) {
// Restore the callback
picker.setOnTimeSetListener(this);
// mPicker = picker;
}
// TODO: Delete this
mNumpad.setKeyListener(this); mNumpad.setKeyListener(this);
mOldAlarmId = getIntent().getLongExtra(EXTRA_ALARM_ID, -1); mOldAlarmId = getIntent().getLongExtra(EXTRA_ALARM_ID, -1);
if (mOldAlarmId != -1) { if (mOldAlarmId != -1) {
// getLoaderManager() for support fragments by default returns the // getLoaderManager() for support fragments by default returns the
@ -116,21 +143,74 @@ public class EditAlarmActivity extends BaseActivity implements
} else { } else {
// Nothing to load, so show default values // Nothing to load, so show default values
showDetails(); showDetails();
// Show the time picker dialog, if it is not already showing
// AND this is the very first time the activity is being created
if (picker == null && savedInstanceState == null) {
// Wait a bit so the activity and dialog don't show at the same time
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
openTimePicker();
}
}, 300);
}
} }
setTimeTextHint(); // TODO: private access setTimeTextHint(); // TODO: private access
} }
@Override @Override
protected void onResume() { protected void onSaveInstanceState(Bundle outState) {
super.onResume(); // Write out any state we wish to save for re-initialization.
// Was the time picker in our backstack? It could have been if it was showing // You can either restore this state in onCreate() or by
// and the device had rotated. // overriding onRestoreInstanceState() and restoring it there.
NumpadTimePickerDialog picker = (NumpadTimePickerDialog) super.onSaveInstanceState(outState);
getSupportFragmentManager().findFragmentByTag(TAG_TIME_PICKER); outState.putString(KEY_INPUT_TIME, mTimeText.getText().toString());
if (picker != null) { // This is restored automatically post-rotation
// Restore the callback outState.putBoolean(KEY_ENABLED, mSwitch.isChecked());
picker.setOnTimeSetListener(this); // These are restored automatically post-rotation
outState.putBooleanArray(KEY_CHECKED_DAYS, new boolean[] {
mDays[0].isChecked(),
mDays[1].isChecked(),
mDays[2].isChecked(),
mDays[3].isChecked(),
mDays[4].isChecked(),
mDays[5].isChecked(),
mDays[6].isChecked()
});
// This is restored automatically post-rotation
outState.putString(KEY_LABEL, mLabel.getText().toString());
outState.putParcelable(KEY_RINGTONE_URI, mSelectedRingtoneUri);
// This is restored automatically post-rotation
outState.putBoolean(KEY_VIBRATE, mVibrate.isChecked());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// For now, restore the previous state here instead of in onCreate()
// because it's easier than refactoring the code over there.
super.onRestoreInstanceState(savedInstanceState);
// No null-check on the given Bundle is necessary,
// because onSaveInstanceState() works with a non-null
// Bundle, even in the default implementation.
if (savedInstanceState.containsKey(KEY_INPUT_TIME)
//&& savedInstanceState.containsKey(KEY_LABEL)
&& savedInstanceState.containsKey(KEY_RINGTONE_URI)) {
// Make sure we actually saved something, or else we'd get
// a null and we'll end up clearing the hints...
mTimeText.setText(savedInstanceState.getString(KEY_INPUT_TIME));
// mLabel.setText(savedInstanceState.getString(KEY_LABEL));
mSelectedRingtoneUri = savedInstanceState.getParcelable(KEY_RINGTONE_URI);
// ...this, however, would throw an NPE because
// we'd be accessing a null Ringtone.
updateRingtoneButtonText();
} }
// TODO: Manually restore the states of the "auto-restoring" widgets.
// In onCreate(), we will call showDetails().
// You only witnessed the auto-restoring for a blank Alarm, where
// the impl of showDetails() is pretty bare. If we have an actual
// Alarm, showDetails() could very well change the values displayed
// by those widgets based on that Alarm's values, but not based on
// any unsaved changes that may have occurred to the widgets previously.
} }
@Override @Override
@ -359,8 +439,8 @@ public class EditAlarmActivity extends BaseActivity implements
@OnClick(R.id.input_time) @OnClick(R.id.input_time)
void openTimePicker() { void openTimePicker() {
NumpadTimePickerDialog picker = NumpadTimePickerDialog.newInstance(EditAlarmActivity.this); NumpadTimePickerDialog.newInstance(EditAlarmActivity.this)
picker.show(getSupportFragmentManager(), TAG_TIME_PICKER); .show(getSupportFragmentManager(), TAG_TIME_PICKER);
} }
private void setWeekDaysText() { private void setWeekDaysText() {
@ -609,7 +689,6 @@ public class EditAlarmActivity extends BaseActivity implements
// TODO default values // TODO default values
showTimeTextFocused(true); showTimeTextFocused(true);
showRingtone(""); // gets default ringtone showRingtone(""); // gets default ringtone
// TODO: Show the dialog instead
//showNumpad(true); //showNumpad(true);
} }
} }

View File

@ -16,6 +16,7 @@ import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import butterknife.OnLongClick; import butterknife.OnLongClick;
import butterknife.OnTouch;
/** /**
* Created by Phillip Hsu on 7/12/2016. * Created by Phillip Hsu on 7/12/2016.
@ -35,7 +36,12 @@ public class NumpadTimePickerDialog extends DialogFragment
private int mInitialMinute; private int mInitialMinute;
private boolean mIs24HourMode; private boolean mIs24HourMode;
/** /**
* The digits stored in the numpad from the last time onSaveInstanceState() was called * The digits stored in the numpad from the last time onSaveInstanceState() was called.
*
* Why not have the NumpadTimePicker class save state itself? Because it's a lot more
* code to do so, as you have to create your own SavedState subclass. Also, we modeled
* this dialog class on the RadialTimePickerDialog, where the RadialPickerLayout also
* depends on the dialog to save its state.
*/ */
private int[] mInputtedDigits; private int[] mInputtedDigits;
@ -97,6 +103,8 @@ public class NumpadTimePickerDialog extends DialogFragment
mNumpad.setOnInputChangeListener(this); mNumpad.setOnInputChangeListener(this);
mNumpad.insertDigits(mInputtedDigits); // TOneverDO: before mNumpad.setOnInputChangeListener(this); mNumpad.insertDigits(mInputtedDigits); // TOneverDO: before mNumpad.setOnInputChangeListener(this);
// Show the cursor immediately
mInputField.requestFocus(); // TODO: If changed to TextView, then don't need this.
// TODO: Disabled color // TODO: Disabled color
updateInputText(""); // Primarily to disable 'OK' updateInputText(""); // Primarily to disable 'OK'
@ -127,6 +135,13 @@ public class NumpadTimePickerDialog extends DialogFragment
updateInputText(""); updateInputText("");
} }
// TODO: If you change the input field to a TextView, then you don't need this.
@OnTouch(R.id.input)
boolean captureTouchOnEditText() {
// Capture touch events on the EditText field, because we want it to do nothing.
return true;
}
@OnClick(R.id.cancel) @OnClick(R.id.cancel)
void myCancel() { void myCancel() {
dismiss(); dismiss();
@ -153,6 +168,8 @@ public class NumpadTimePickerDialog extends DialogFragment
private void updateInputText(String inputText) { private void updateInputText(String inputText) {
mInputField.setText(inputText); mInputField.setText(inputText);
// Move the cursor
//mInputField.setSelection(mInputField.length()); // TODO: If changed to TextView, don't need this
mOkButton.setEnabled(mNumpad.checkTimeValid()); mOkButton.setEnabled(mNumpad.checkTimeValid());
} }
} }

View File

@ -5,24 +5,29 @@
<ImageButton <ImageButton
android:id="@+id/backspace" android:id="@+id/backspace"
android:layout_width="72dp" android:layout_width="@dimen/header_height"
android:layout_height="72dp" android:layout_height="@dimen/header_height"
android:src="@drawable/ic_backspace_24dp" android:src="@drawable/ic_backspace_24dp"
android:background="?selectableItemBackground" android:background="?selectableItemBackground"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"/> android:layout_alignParentTop="true"/>
<!-- TODO: Consider changing to TextView if you don't
want the cursor after all. A reason you may want to do
so is the cursor is getting cut off, most likely because
of the text size being larger than the view bounds. You can
probably solve this by adding some padding. Another reason is
the cursor looks ugly with it blinking. -->
<EditText <EditText
android:id="@+id/input" android:id="@+id/input"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="72dp" android:layout_height="@dimen/header_height"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textSize="40sp" android:textSize="@dimen/time_input_text_size"
android:focusable="false" android:focusable="false"
android:focusableInTouchMode="false" android:focusableInTouchMode="false"/>
android:cursorVisible="true"/>
<View <View
android:id="@+id/divider" android:id="@+id/divider"
@ -31,13 +36,10 @@
android:background="?android:listDivider" android:background="?android:listDivider"
android:layout_below="@id/input"/> android:layout_below="@id/input"/>
<!-- TODO: Your height attr is being ignored because
you constrained this below and above the header and
footer views -->
<com.philliphsu.clock2.editalarm.NumpadTimePicker <com.philliphsu.clock2.editalarm.NumpadTimePicker
android:id="@+id/number_grid" android:id="@+id/number_grid"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="270dp" android:layout_height="@dimen/numpad_height"
android:layout_below="@id/divider"/> android:layout_below="@id/divider"/>
<LinearLayout <LinearLayout

View File

@ -11,4 +11,9 @@
<dimen name="alarm_time_text_size">48sp</dimen> <dimen name="alarm_time_text_size">48sp</dimen>
<dimen name="app_bar_height">180dp</dimen> <dimen name="app_bar_height">180dp</dimen>
<dimen name="text_margin">16dp</dimen> <dimen name="text_margin">16dp</dimen>
<!-- NumpadTimePickerDialog -->
<dimen name="header_height">72dp</dimen>
<dimen name="numpad_height">270dp</dimen>
<dimen name="time_input_text_size">40sp</dimen>
</resources> </resources>