diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java index a57a574..bcf56ac 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java @@ -4,6 +4,7 @@ import android.content.Intent; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.StringRes; import android.support.design.widget.CoordinatorLayout; 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 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 ID_MENU_ITEM = 0; @@ -82,6 +90,12 @@ public class EditAlarmActivity extends BaseActivity implements private int mSelectedHourOfDay = -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.save) Button mSave; @Bind(R.id.delete) Button mDelete; @@ -105,7 +119,20 @@ public class EditAlarmActivity extends BaseActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 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); + mOldAlarmId = getIntent().getLongExtra(EXTRA_ALARM_ID, -1); if (mOldAlarmId != -1) { // getLoaderManager() for support fragments by default returns the @@ -116,21 +143,74 @@ public class EditAlarmActivity extends BaseActivity implements } else { // Nothing to load, so show default values 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 } @Override - protected void onResume() { - super.onResume(); - // Was the time picker in our backstack? It could have been if it was showing - // and the device had rotated. - NumpadTimePickerDialog picker = (NumpadTimePickerDialog) - getSupportFragmentManager().findFragmentByTag(TAG_TIME_PICKER); - if (picker != null) { - // Restore the callback - picker.setOnTimeSetListener(this); + protected void onSaveInstanceState(Bundle outState) { + // Write out any state we wish to save for re-initialization. + // You can either restore this state in onCreate() or by + // overriding onRestoreInstanceState() and restoring it there. + super.onSaveInstanceState(outState); + outState.putString(KEY_INPUT_TIME, mTimeText.getText().toString()); + // This is restored automatically post-rotation + outState.putBoolean(KEY_ENABLED, mSwitch.isChecked()); + // 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 @@ -359,8 +439,8 @@ public class EditAlarmActivity extends BaseActivity implements @OnClick(R.id.input_time) void openTimePicker() { - NumpadTimePickerDialog picker = NumpadTimePickerDialog.newInstance(EditAlarmActivity.this); - picker.show(getSupportFragmentManager(), TAG_TIME_PICKER); + NumpadTimePickerDialog.newInstance(EditAlarmActivity.this) + .show(getSupportFragmentManager(), TAG_TIME_PICKER); } private void setWeekDaysText() { @@ -609,7 +689,6 @@ public class EditAlarmActivity extends BaseActivity implements // TODO default values showTimeTextFocused(true); showRingtone(""); // gets default ringtone - // TODO: Show the dialog instead //showNumpad(true); } } diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java index 39c3a14..ba8150f 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java @@ -16,6 +16,7 @@ import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.OnLongClick; +import butterknife.OnTouch; /** * Created by Phillip Hsu on 7/12/2016. @@ -35,7 +36,12 @@ public class NumpadTimePickerDialog extends DialogFragment private int mInitialMinute; 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; @@ -97,6 +103,8 @@ public class NumpadTimePickerDialog extends DialogFragment 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 updateInputText(""); // Primarily to disable 'OK' @@ -127,6 +135,13 @@ public class NumpadTimePickerDialog extends DialogFragment 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) void myCancel() { dismiss(); @@ -153,6 +168,8 @@ public class NumpadTimePickerDialog extends DialogFragment private void updateInputText(String inputText) { mInputField.setText(inputText); + // Move the cursor + //mInputField.setSelection(mInputField.length()); // TODO: If changed to TextView, don't need this mOkButton.setEnabled(mNumpad.checkTimeValid()); } } diff --git a/app/src/main/res/layout/dialog_time_picker_numpad.xml b/app/src/main/res/layout/dialog_time_picker_numpad.xml index cc5e73b..106589b 100644 --- a/app/src/main/res/layout/dialog_time_picker_numpad.xml +++ b/app/src/main/res/layout/dialog_time_picker_numpad.xml @@ -5,24 +5,29 @@ + + android:focusableInTouchMode="false"/> - 48sp 180dp 16dp + + + 72dp + 270dp + 40sp