From a07cf222b3dfdfb15bd34d15ad3a2cb2f5296fe5 Mon Sep 17 00:00:00 2001 From: Phillip Hsu Date: Wed, 20 Jul 2016 19:42:05 -0700 Subject: [PATCH] Moved alt buttons and fab to dialog layout --- .../editalarm/BaseTimePickerDialog.java | 81 ++++----- .../clock2/editalarm/GridLayoutNumpad.java | 27 +-- .../clock2/editalarm/NumpadTimePicker.java | 169 ++++++++++-------- .../editalarm/NumpadTimePickerDialog.java | 44 +++-- .../res/layout/content_grid_layout_numpad.xml | 1 - .../res/layout/content_numpad_time_picker.xml | 41 ----- .../res/layout/dialog_time_picker_numpad.xml | 100 ++++++++--- 7 files changed, 252 insertions(+), 211 deletions(-) delete mode 100644 app/src/main/res/layout/content_numpad_time_picker.xml diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/BaseTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/editalarm/BaseTimePickerDialog.java index fad494e..7a0a89d 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/BaseTimePickerDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/BaseTimePickerDialog.java @@ -1,22 +1,20 @@ package com.philliphsu.clock2.editalarm; -import android.app.Dialog; -import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.LayoutRes; -import android.support.design.widget.BottomSheetBehavior; -import android.support.design.widget.BottomSheetDialog; -import android.support.design.widget.BottomSheetDialogFragment; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; import butterknife.ButterKnife; /** * Created by Phillip Hsu on 7/16/2016. */ -public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment { +public abstract class BaseTimePickerDialog extends DialogFragment { // TODO: Consider private access, and then writing package/protected API that subclasses // can use to interface with this field. @@ -35,42 +33,42 @@ public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment { mCallback = callback; } - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Dialog dialog = super.onCreateDialog(savedInstanceState); - // We're past onCreate() in the lifecycle, so we can safely retrieve the host activity. - View view = LayoutInflater.from(getActivity()).inflate(contentLayout(), null); - /** - * Adds our view to a ViewGroup that has a BottomSheetBehavior attached. The ViewGroup - * itself is a child of a CoordinatorLayout. - * @see {@link BottomSheetDialog#wrapInBottomSheet(int, View, ViewGroup.LayoutParams)} - */ - dialog.setContentView(view); - // Bind this fragment, not the internal dialog! - ButterKnife.bind(this, view); - final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent()); - // When we collapse, collapse all the way. Do not be misled by the "docs" in - // https://android-developers.blogspot.com.au/2016/02/android-support-library-232.html - // when it says: - // "STATE_COLLAPSED: ... the app:behavior_peekHeight attribute (defaults to 0)" - // While it is true by default, BottomSheetDialogs override this default height. - // See http://stackoverflow.com/a/35634293/5055032 for an alternative solution involving - // defining a style that overrides the attribute. - // TODO: If the sheet is dragged out of view, then the screen remains darkened until - // a subsequent touch on the screen. Consider doing the alt. soln.? - behavior.setPeekHeight(0); - dialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - // Every time we show, show at our full height. - behavior.setState(BottomSheetBehavior.STATE_EXPANDED); - } - }); +// Code for BottomSheetDialogs only. To uncomment, highlight and CTRL + / +// @Override +// public Dialog onCreateDialog(Bundle savedInstanceState) { +// Dialog dialog = super.onCreateDialog(savedInstanceState); +// // We're past onCreate() in the lifecycle, so we can safely retrieve the host activity. +// View view = LayoutInflater.from(getActivity()).inflate(contentLayout(), null); +// /** +// * Adds our view to a ViewGroup that has a BottomSheetBehavior attached. The ViewGroup +// * itself is a child of a CoordinatorLayout. +// * @see {@link BottomSheetDialog#wrapInBottomSheet(int, View, ViewGroup.LayoutParams)} +// */ +// dialog.setContentView(view); +// // Bind this fragment, not the internal dialog! +// ButterKnife.bind(this, view); +// final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent()); +// // When we collapse, collapse all the way. Do not be misled by the "docs" in +// // https://android-developers.blogspot.com.au/2016/02/android-support-library-232.html +// // when it says: +// // "STATE_COLLAPSED: ... the app:behavior_peekHeight attribute (defaults to 0)" +// // While it is true by default, BottomSheetDialogs override this default height. +// // See http://stackoverflow.com/a/35634293/5055032 for an alternative solution involving +// // defining a style that overrides the attribute. +// // TODO: If the sheet is dragged out of view, then the screen remains darkened until +// // a subsequent touch on the screen. Consider doing the alt. soln.? +// behavior.setPeekHeight(0); +// dialog.setOnShowListener(new DialogInterface.OnShowListener() { +// @Override +// public void onShow(DialogInterface dialog) { +// // Every time we show, show at our full height. +// behavior.setState(BottomSheetBehavior.STATE_EXPANDED); +// } +// }); +// +// return dialog; +// } - return dialog; - } - - /* @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -79,7 +77,6 @@ public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment { ButterKnife.bind(this, view); return view; } - */ @Override public void onDestroyView() { diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java b/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java index f0c1c6f..da09600 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java @@ -2,12 +2,11 @@ package com.philliphsu.clock2.editalarm; import android.content.Context; import android.support.annotation.CallSuper; -import android.support.annotation.LayoutRes; import android.support.v7.widget.GridLayout; import android.util.AttributeSet; import android.view.View; -import android.widget.Button; import android.widget.ImageButton; +import android.widget.TextView; import com.philliphsu.clock2.R; @@ -26,7 +25,7 @@ import butterknife.OnLongClick; * and not the backspace button. However, we do provide an API for removing * digits from the input. */ -public abstract class GridLayoutNumpad extends GridLayout implements View.OnClickListener { +public abstract class GridLayoutNumpad extends GridLayout { // TODO: change to private? protected static final int UNMODIFIED = -1; private static final int COLUMNS = 3; @@ -37,7 +36,7 @@ public abstract class GridLayoutNumpad extends GridLayout implements View.OnClic @Bind({ R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four, R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine }) - Button[] mButtons; + TextView[] mButtons; @Bind(R.id.backspace) ImageButton mBackspace; /** @@ -72,12 +71,6 @@ public abstract class GridLayoutNumpad extends GridLayout implements View.OnClic */ public abstract int capacity(); - /** - * @return the layout resource that defines the children for this numpad - */ - @LayoutRes - protected abstract int contentLayout(); - public final void setOnInputChangeListener(OnInputChangeListener onInputChangeListener) { mOnInputChangeListener = onInputChangeListener; } @@ -172,7 +165,7 @@ public abstract class GridLayoutNumpad extends GridLayout implements View.OnClic * {@link OnInputChangeListener OnInputChangeListener} * after a digit insertion. By default, the String * forwarded is just the String value of the inserted digit. - * @see #onClick(View) + * @see #onClick(TextView) * @param newDigit the formatted String that should be displayed */ @CallSuper @@ -233,11 +226,11 @@ public abstract class GridLayoutNumpad extends GridLayout implements View.OnClic } } - // TODO: Why not @OnClick instead? - @Override - public final void onClick(View v) { + @OnClick({ R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four, + R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine }) + final void onClick(TextView view) { if (mCount < mInput.length) { - String textNum = ((Button) v).getText().toString(); + String textNum = view.getText().toString(); insertDigits(Integer.parseInt(textNum)); } } @@ -245,10 +238,8 @@ public abstract class GridLayoutNumpad extends GridLayout implements View.OnClic private void init() { setAlignmentMode(ALIGN_BOUNDS); setColumnCount(COLUMNS); - View.inflate(getContext(), contentLayout(), this); + View.inflate(getContext(), R.layout.content_grid_layout_numpad, this); ButterKnife.bind(this); - for (Button b : mButtons) - b.setOnClickListener(this); // If capacity() < 0, we let the system throw the exception. mInput = new int[capacity()]; Arrays.fill(mInput, UNMODIFIED); diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePicker.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePicker.java index 3ee40a7..3ffdcfd 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePicker.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePicker.java @@ -5,17 +5,13 @@ import android.support.annotation.IntDef; import android.support.design.widget.FloatingActionButton; import android.text.format.DateFormat; import android.util.AttributeSet; -import android.widget.Button; - -import com.philliphsu.clock2.R; +import android.view.View; +import android.widget.TextView; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.text.DateFormatSymbols; -import java.util.Calendar; - -import butterknife.Bind; -import butterknife.OnClick; /** * Created by Phillip Hsu on 7/12/2016. @@ -46,10 +42,10 @@ public class NumpadTimePicker extends GridLayoutNumpad implements TimePicker { @AmPmState private int mAmPmState = UNSPECIFIED; private final StringBuilder mFormattedInput = new StringBuilder(MAX_CHARS); - - @Bind({ R.id.leftAlt, R.id.rightAlt }) - Button[] mAltButtons; - @Bind(R.id.fab) FloatingActionButton mFab; + + private WeakReference mLeftAlt; + private WeakReference mRightAlt; + private WeakReference mFab; /** * Provides additional APIs to configure clients' display output. @@ -77,18 +73,17 @@ public class NumpadTimePicker extends GridLayoutNumpad implements TimePicker { return MAX_DIGITS; } - @Override - protected int contentLayout() { - return R.layout.content_numpad_time_picker; - } - @Override protected void enable(int lowerLimitInclusive, int upperLimitExclusive) { super.enable(lowerLimitInclusive, upperLimitExclusive); if (lowerLimitInclusive == 0 && upperLimitExclusive == 0) { // For 12-hour clock, alt buttons need to be disabled as well before firing onInputDisabled() - if (!is24HourFormat() && (mAltButtons[0].isEnabled() || mAltButtons[1].isEnabled())) { - return; + if (!is24HourFormat()) { + if (mLeftAlt != null && mLeftAlt.get() != null + && mRightAlt != null && mRightAlt.get() != null + && (mLeftAlt.get().isEnabled() || mRightAlt.get().isEnabled())) { + return; + } } ((OnInputChangeListener) getOnInputChangeListener()).onInputDisabled(); } @@ -236,8 +231,9 @@ public class NumpadTimePicker extends GridLayoutNumpad implements TimePicker { } mAmPmState = amPmState; - if (mAmPmState != HRS_24) { - onAltButtonClick(mAmPmState == AM ? mAltButtons[0] : mAltButtons[1]); + if (mAmPmState != HRS_24 && mLeftAlt != null && mRightAlt != null + && mLeftAlt.get() != null && mRightAlt.get() != null) { + mAltButtonClickListener.onClick(mAmPmState == AM ? mLeftAlt.get() : mRightAlt.get()); } } @@ -245,55 +241,81 @@ public class NumpadTimePicker extends GridLayoutNumpad implements TimePicker { return mFormattedInput.toString(); } + /** + * This was only useful when the FAB was part of the numpad's layout, and the dialog + * wanted to listen to clicks on it. Now that the dialog contains the FAB, there is + * no use for this. + * @deprecated Pass in a FAB with {@link #setFab(FloatingActionButton)} instead. + */ + @Deprecated public void setFabClickListener(OnClickListener fabClickListener) { - mFab.setOnClickListener(fabClickListener); +// mFab.setOnClickListener(fabClickListener); + } + + public void setFab(FloatingActionButton fab) { + mFab = new WeakReference<>(fab); + updateNumpadStates(); + } + + public void setAltButtons(TextView leftAlt, TextView rightAlt) { + mLeftAlt = new WeakReference<>(leftAlt); + mRightAlt = new WeakReference<>(rightAlt); + mLeftAlt.get().setOnClickListener(mAltButtonClickListener); + mRightAlt.get().setOnClickListener(mAltButtonClickListener); + updateNumpadStates(); } private void init() { - if (is24HourFormat()) { - mAltButtons[0].setText(R.string.left_alt_24hr); - mAltButtons[1].setText(R.string.right_alt_24hr); - } else { - String[] amPmTexts = new DateFormatSymbols().getAmPmStrings(); - mAltButtons[0].setText(amPmTexts[Calendar.AM]); - mAltButtons[1].setText(amPmTexts[Calendar.PM]); - } + /* This was where we set the text on the alt buttons, when they were still part of our layout */ updateNumpadStates(); } - @OnClick({ R.id.leftAlt, R.id.rightAlt }) - void onAltButtonClick(Button altBtn) { - if (mAltButtons[0] != altBtn && mAltButtons[1] != altBtn) - throw new IllegalArgumentException("Not called with one of the alt buttons"); + private final AltButtonClickListener mAltButtonClickListener = new AltButtonClickListener(this); - // Manually insert special characters for 12-hour clock - if (!is24HourFormat()) { - if (count() <= 2) { - // The colon is inserted for you - insertDigits(0, 0); - } - // text is AM or PM, so include space before - mFormattedInput.append(' ').append(altBtn.getText()); - mAmPmState = mAltButtons[0] == altBtn ? AM : PM; - // Digits will be shown for you on insert, but not AM/PM -/*TOneverDO: remove super*/super.onDigitInserted(mFormattedInput.toString()); - } else { - CharSequence text = altBtn.getText(); - int[] digits = new int[text.length() - 1]; - // charAt(0) is the colon, so skip i = 0. - // We are only interested in storing the digits. - for (int i = 1; i < text.length(); i++) { - // The array and the text do not have the same lengths, - // so the iterator value does not correspond to the - // array index directly - digits[i - 1] = Character.digit(text.charAt(i), BASE_10); - } - // Colon is added for you - insertDigits(digits); - mAmPmState = HRS_24; + private static class AltButtonClickListener implements OnClickListener { + private final WeakReference mPicker; + + public AltButtonClickListener(NumpadTimePicker picker) { + mPicker = new WeakReference<>(picker); } - updateNumpadStates(); + @Override + public void onClick(View v) { + TextView altBtn = (TextView) v; + + // Manually insert special characters for 12-hour clock + if (!mPicker.get().is24HourFormat()) { + if (mPicker.get().count() <= 2) { + // The colon is inserted for you + mPicker.get().insertDigits(0, 0); + } + // text is AM or PM, so include space before + String ampm = altBtn.getText().toString(); + StringBuilder mFormattedInput = mPicker.get().mFormattedInput; + mFormattedInput.append(' ').append(ampm); + String am = new DateFormatSymbols().getAmPmStrings()[0]; + mPicker.get().mAmPmState = ampm.equals(am) ? AM : PM; + // Digits will be shown for you on insert, but not AM/PM + ((/*NOT REDUNDANT! TOneverDO: Remove cast*/GridLayoutNumpad) + mPicker.get()).onDigitInserted(mFormattedInput.toString()); + } else { + CharSequence text = altBtn.getText(); + int[] digits = new int[text.length() - 1]; + // charAt(0) is the colon, so skip i = 0. + // We are only interested in storing the digits. + for (int i = 1; i < text.length(); i++) { + // The array and the text do not have the same lengths, + // so the iterator value does not correspond to the + // array index directly + digits[i - 1] = Character.digit(text.charAt(i), BASE_10); + } + // Colon is added for you + mPicker.get().insertDigits(digits); + mPicker.get().mAmPmState = HRS_24; + } + + mPicker.get().updateNumpadStates(); + } } private boolean is24HourFormat() { @@ -378,7 +400,8 @@ public class NumpadTimePicker extends GridLayoutNumpad implements TimePicker { } private void updateFabState() { - mFab.setEnabled(checkTimeValid()); + if (mFab != null && mFab.get() != null) + mFab.get().setEnabled(checkTimeValid()); } private void updateBackspaceState() { @@ -386,39 +409,41 @@ public class NumpadTimePicker extends GridLayoutNumpad implements TimePicker { } private void updateAltButtonStates() { + if (mLeftAlt == null || mRightAlt == null || mLeftAlt.get() == null || mRightAlt.get() == null) + return; if (count() == 0) { // No input, no access! - mAltButtons[0].setEnabled(false); - mAltButtons[1].setEnabled(false); + mLeftAlt.get().setEnabled(false); + mRightAlt.get().setEnabled(false); } else if (count() == 1) { // Any of 0-9 inputted, always have access in either clock. - mAltButtons[0].setEnabled(true); - mAltButtons[1].setEnabled(true); + mLeftAlt.get().setEnabled(true); + mRightAlt.get().setEnabled(true); } else if (count() == 2) { // Any 2 digits that make a valid hour for either clock are eligible for access int time = getInput(); boolean validTwoDigitHour = is24HourFormat() ? time <= 23 : time >= 10 && time <= 12; - mAltButtons[0].setEnabled(validTwoDigitHour); - mAltButtons[1].setEnabled(validTwoDigitHour); + mLeftAlt.get().setEnabled(validTwoDigitHour); + mRightAlt.get().setEnabled(validTwoDigitHour); } else if (count() == 3) { if (is24HourFormat()) { // For the 24-hour clock, no access at all because // two more digits (00 or 30) cannot be added to 3 digits. - mAltButtons[0].setEnabled(false); - mAltButtons[1].setEnabled(false); + mLeftAlt.get().setEnabled(false); + mRightAlt.get().setEnabled(false); } else { // True for any 3 digits, if AM/PM not already entered boolean enabled = mAmPmState == UNSPECIFIED; - mAltButtons[0].setEnabled(enabled); - mAltButtons[1].setEnabled(enabled); + mLeftAlt.get().setEnabled(enabled); + mRightAlt.get().setEnabled(enabled); } } else if (count() == MAX_DIGITS) { // If all 4 digits are filled in, the 24-hour clock has absolutely // no need for the alt buttons. However, The 12-hour clock has // complete need of them, if not already used. boolean enabled = !is24HourFormat() && mAmPmState == UNSPECIFIED; - mAltButtons[0].setEnabled(enabled); - mAltButtons[1].setEnabled(enabled); + mLeftAlt.get().setEnabled(enabled); + mRightAlt.get().setEnabled(enabled); } } 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 07056d8..b4a9495 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java @@ -1,14 +1,21 @@ package com.philliphsu.clock2.editalarm; import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.EditText; import com.philliphsu.clock2.R; +import java.text.DateFormatSymbols; +import java.util.Calendar; + import butterknife.Bind; +import butterknife.OnClick; import butterknife.OnTouch; /** @@ -40,6 +47,9 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog @Bind(R.id.input_time) EditText mInputField; @Bind(R.id.number_grid) NumpadTimePicker mNumpad; @Bind(R.id.focus_grabber) View mFocusGrabber; + @Bind({ R.id.leftAlt, R.id.rightAlt }) + Button[] mAltButtons; + @Bind(R.id.fab) FloatingActionButton mFab; // TODO: We don't need to pass in an initial hour and minute for a new instance. // TODO: Delete is24HourMode? @@ -79,18 +89,11 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); - // Can't do a method bind because the FAB is not part of this dialog's layout - // Also can't do the bind in the Numpad's class, because it doesn't have access to - // the OnTimeSetListener callback contained here or the dialog's dismiss() - mNumpad.setFabClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mNumpad.checkTimeValid()) - return; - mCallback.onTimeSet(mNumpad, mNumpad.hourOfDay(), mNumpad.minute()); - dismiss(); - } - }); + + // Pass in our views so numpad can control their states for us + mNumpad.setFab(mFab); + mNumpad.setAltButtons(mAltButtons[0], mAltButtons[1]); + mNumpad.setOnInputChangeListener(this); mNumpad.insertDigits(mInputtedDigits); // TOneverDO: before mNumpad.setOnInputChangeListener(this); // Show the cursor immediately @@ -98,6 +101,15 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog // TODO: Disabled color //updateInputText(""); // Primarily to disable 'OK' + if (DateFormat.is24HourFormat(getActivity())) { + mAltButtons[0].setText(R.string.left_alt_24hr); + mAltButtons[1].setText(R.string.right_alt_24hr); + } else { + String[] amPmTexts = new DateFormatSymbols().getAmPmStrings(); + mAltButtons[0].setText(amPmTexts[Calendar.AM]); + mAltButtons[1].setText(amPmTexts[Calendar.PM]); + } + return view; } @@ -142,6 +154,14 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog return true; } + @OnClick(R.id.fab) + void confirmSelection() { + if (!mNumpad.checkTimeValid()) + return; + mCallback.onTimeSet(mNumpad, mNumpad.hourOfDay(), mNumpad.minute()); + dismiss(); + } + private void updateInputText(String inputText) { TimeTextUtils.setText(inputText, mInputField); // Move the cursor diff --git a/app/src/main/res/layout/content_grid_layout_numpad.xml b/app/src/main/res/layout/content_grid_layout_numpad.xml index 1616ab0..9b87651 100644 --- a/app/src/main/res/layout/content_grid_layout_numpad.xml +++ b/app/src/main/res/layout/content_grid_layout_numpad.xml @@ -52,7 +52,6 @@ grid:layout_column="1" android:text="0"/> - - - - - - - - - -