diff --git a/app/build.gradle b/app/build.gradle index 5646364..7845cc5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -90,5 +90,5 @@ dependencies { compile 'com.android.support:gridlayout-v7:24.2.0' compile 'com.android.support:cardview-v7:24.2.0' compile 'com.jakewharton:butterknife:7.0.1' - compile project(":bottomsheetpickers-release") + compile 'com.philliphsu:bottomsheetpickers:2.3.0' } diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/AccessibleTextView.java b/app/src/main/java/com/philliphsu/clock2/timepickers/AccessibleTextView.java deleted file mode 100644 index 924e1f8..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/AccessibleTextView.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.Button; -import android.widget.TextView; - -/** - * Fake Button class, used so TextViews can announce themselves as Buttons, for accessibility. - */ -public class AccessibleTextView extends TextView { - - public AccessibleTextView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - event.setClassName(Button.class.getName()); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setClassName(Button.class.getName()); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/BaseTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/timepickers/BaseTimePickerDialog.java deleted file mode 100644 index 1ccbb8a..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/BaseTimePickerDialog.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.Nullable; -import android.support.design.widget.BottomSheetDialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import butterknife.ButterKnife; - -/** - * Created by Phillip Hsu on 7/16/2016. - */ -public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment { - private static final String TAG = "BaseTimePickerDialog"; - - private OnTimeSetListener mCallback; - - /** - * The callback interface used to indicate the user is done filling in - * the time (they clicked on the 'Set' button). - */ - public interface OnTimeSetListener { - /** - * @param viewGroup The view associated with this listener. - * @param hourOfDay The hour that was set. - * @param minute The minute that was set. - */ - // TODO: Consider removing VG param, since listeners probably won't need to use it.... - void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute); - } - - /** - * Empty constructor required for dialog fragment. - * Subclasses do not need to write their own. - */ - public BaseTimePickerDialog() {} - - @LayoutRes - protected abstract int contentLayout(); - - public final void setOnTimeSetListener(OnTimeSetListener callback) { - mCallback = callback; - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - // Not needed for bottom sheet dialogs -// getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - final View view = inflater.inflate(contentLayout(), container, false); - ButterKnife.bind(this, view); - - // TODO: We could move this to onCreateDialog() if we cared. - // - // onShow() is called immediately as this DialogFragment is showing, so the - // FAB's animation will barely be noticeable. -// getDialog().setOnShowListener(new DialogInterface.OnShowListener() { -// @Override -// public void onShow(DialogInterface dialog) { -// Log.i(TAG, "onShow()"); -// // Animate the FAB into view -// View v = view.findViewById(R.id.fab); -// if (v != null) { -// FloatingActionButton fab = (FloatingActionButton) v; -// fab.show(); -// } -// } -// }); - - return view; - } - - protected final void onTimeSet(ViewGroup vg, int hourOfDay, int minute) { - if (mCallback != null) { - mCallback.onTimeSet(vg, hourOfDay, minute); - } - dismiss(); - } - -// @Override -// public void onResume() { -// super.onResume(); -// final View view = getView(); -// final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent()); -// // Copy over the internal callback logic, and also implement our own -// // -// // This callback is set AFTER this Fragment has become visible, so is useless for what -// // you wanted to do (show the FAB during the settling phase). -// behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { -// @Override -// public void onStateChanged(@NonNull View bottomSheet, int newState) { -// Log.i(TAG, "onStateChanged(): " + newState); -// if (newState == BottomSheetBehavior.STATE_HIDDEN) { -// dismiss(); -// } -// // My logic below -// else if (newState == BottomSheetBehavior.STATE_SETTLING) { -// View fab = view.findViewById(R.id.fab); -// if (fab != null) { -// ((FloatingActionButton) fab).show(); -// } -// } -// } -// -// @Override -// public void onSlide(@NonNull View bottomSheet, float slideOffset) { -// -// } -// }); -// } - - @Override - public void onDestroyView() { - super.onDestroyView(); - ButterKnife.unbind(this); - } - - // Code for AlertDialog style only. -// @NonNull -// @Override -// public Dialog onCreateDialog(Bundle savedInstanceState) { -// // Use an AlertDialog to display footer buttons, rather than -// // re-invent them in our layout. -// AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); -// builder.setView(contentLayout()) -// // The action strings are already defined and localized by the system! -// .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { -// @Override -// public void onClick(DialogInterface dialog, int which) { -// -// } -// }) -// .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { -// @Override -// public void onClick(DialogInterface dialog, int which) { -// -// } -// }); -// return builder.create(); -// } - - // This was an unsatisfactory solution to forcing the bottom sheet to show at its - // fully expanded state. Our anchored FAB and GridLayout buttons would not be visible. -// @Override -// public Dialog onCreateDialog(Bundle savedInstanceState) { -// Dialog dialog = super.onCreateDialog(savedInstanceState); -// //dialog = new BottomSheetDialog(getActivity(), R.style.AppTheme_AppCompatDialog/*crashes our app!*/); -// // We're past onCreate() in the lifecycle, so the activity is alive. -// 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! (There is a bind(Dialog) API.) -// 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. - - // This means the sheet is considered "open" even at a height of 0! This is why -// // when you swipe to hide the sheet, the screen remains darkened--indicative -// // of an open dialog. -// 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; -// } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/GridLayoutNumpad.java b/app/src/main/java/com/philliphsu/clock2/timepickers/GridLayoutNumpad.java deleted file mode 100644 index 3883d70..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/GridLayoutNumpad.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.support.annotation.CallSuper; -import android.support.annotation.LayoutRes; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.GridLayout; -import android.util.AttributeSet; -import android.view.View; -import android.widget.TextView; - -import com.philliphsu.clock2.R; - -import java.util.Arrays; - -import butterknife.Bind; -import butterknife.ButterKnife; -import butterknife.OnClick; - -/** - * Created by Phillip Hsu on 7/12/2016. - * - * Successor to the Numpad class that was based on TableLayout. - * - * TODO: Is NumpadTimePicker the only subclass? If so, why do we need this - * superclass? If we move the contents of this class to NumpadTimePicker, - * the implementation of setTheme() would make more sense. - */ -public abstract class GridLayoutNumpad extends GridLayout { - // TODO: change to private? - protected static final int UNMODIFIED = -1; - private static final int COLUMNS = 3; - - private int[] mInput; - private int mCount = 0; - private OnInputChangeListener mOnInputChangeListener; - - private ColorStateList mTextColors; - int mAccentColor; - - @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 }) - TextView[] mButtons; - - /** - * Informs clients how to output the digits inputted into this numpad. - */ - public interface OnInputChangeListener { - /** - * @param newStr the new value of the input formatted as a - * String after a digit insertion - */ - void onDigitInserted(String newStr); - /** - * @param newStr the new value of the input formatted as a - * String after a digit deletion - */ - void onDigitDeleted(String newStr); - void onDigitsCleared(); - } - - public GridLayoutNumpad(Context context) { - this(context, null); - } - - public GridLayoutNumpad(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - void setTheme(Context context, boolean themeDark) { - // Since the Dialog class already set the background color of its entire view tree, - // our background is already colored. Why did we set it in the Dialog class? Because - // we use margins around the numpad, and if we had instead set the background on - // this numpad here, the margins will not be colored. Why not use padding instead - // of margins? It turns out we tried that--replacing each margin attribute - // with the padding counterpart--but we lost the pre-21 FAB inherent bottom margin. - - // The buttons are actually of type Button, but we kept references - // to them as TextViews... which is fine since TextView is the superclass - // of Button. - mTextColors = ContextCompat.getColorStateList(context, themeDark? - R.color.numeric_keypad_button_text_dark : R.color.numeric_keypad_button_text); - - // AFAIK, the only way to get the user's accent color is programmatically, - // because it is uniquely defined in their app's theme. It is not possible - // for us to reference that via XML (i.e. with ?colorAccent or similar), - // which happens at compile time. - // TOneverDO: Use any other Context to retrieve the accent color. We must use - // the Context param passed to us, because we know this context to be - // NumpadTimePickerDialog.getContext(), which is equivalent to - // NumpadTimePickerDialog.getActivity(). It is from that Activity where we - // get its theme's colorAccent. - mAccentColor = Utils.getThemeAccentColor(context); - for (TextView b : mButtons) { - setTextColor(b); - Utils.setColorControlHighlight(b, mAccentColor); - } - } - - void setTextColor(TextView view) { - view.setTextColor(mTextColors); - } - - /** - * @return the number of digits we can input - */ - public abstract int capacity(); - - @LayoutRes - protected abstract int contentLayout(); - - public final void setOnInputChangeListener(OnInputChangeListener onInputChangeListener) { - mOnInputChangeListener = onInputChangeListener; - } - - /** - * Provided only for subclasses so they can retrieve the registered listener - * and fire any custom OnInputChange events they may have defined. - */ - protected final OnInputChangeListener getOnInputChangeListener() { - return mOnInputChangeListener; - } - - @CallSuper - protected void enable(int lowerLimitInclusive, int upperLimitExclusive) { - if (lowerLimitInclusive < 0 || upperLimitExclusive > mButtons.length) - throw new IndexOutOfBoundsException("Upper limit out of range"); - - for (int i = 0; i < mButtons.length; i++) - mButtons[i].setEnabled(i >= lowerLimitInclusive && i < upperLimitExclusive); - } - - protected final int valueAt(int index) { - return mInput[index]; - } - - /** - * @return a defensive copy of the internal array of inputted digits - */ - protected final int[] getDigits() { - int[] digits = new int[mInput.length]; - System.arraycopy(mInput, 0, digits, 0, mInput.length); - return digits; - } - - /** - * @return the number of digits inputted - */ - public final int count() { - return mCount; - } - - /** - * @return the integer represented by the inputted digits - */ - protected final int getInput() { - return Integer.parseInt(getInputString()); - } - - private String getInputString() { - String currentInput = ""; - for (int i : mInput) - if (i != UNMODIFIED) - currentInput += i; - return currentInput; - } - - public void delete() { - /* - if (mCount - 1 >= 0) { - mInput[--mCount] = UNMODIFIED; - } - onDigitDeleted(getInputString()); - */ - delete(mCount); - } - - // TODO: Why do we need this? - @Deprecated - public void delete(int at) { - if (at - 1 >= 0) { - mInput[at - 1] = UNMODIFIED; - mCount--; - onDigitDeleted(getInputString()); - } - } - - public boolean clear() { - Arrays.fill(mInput, UNMODIFIED); - mCount = 0; - onDigitsCleared(); - return true; - } - - /** - * Forwards the provided String to the assigned - * {@link OnInputChangeListener OnInputChangeListener} - * after a digit insertion. By default, the String - * forwarded is just the String value of the inserted digit. - * @see #onClick(TextView) - * @param newDigit the formatted String that should be displayed - */ - @CallSuper - protected void onDigitInserted(String newDigit) { - if (mOnInputChangeListener != null) { - mOnInputChangeListener.onDigitInserted(newDigit); - } - } - - /** - * Forwards the provided String to the assigned - * {@link OnInputChangeListener OnInputChangeListener} - * after a digit deletion. By default, the String - * forwarded is {@link #getInputString()}. - * @param newStr the formatted String that should be displayed - */ - @CallSuper - protected void onDigitDeleted(String newStr) { - if (mOnInputChangeListener != null) { - mOnInputChangeListener.onDigitDeleted(newStr); - } - } - - /** - * Forwards a {@code onDigitsCleared()} event to the assigned - * {@link OnInputChangeListener OnInputChangeListener}. - */ - @CallSuper - protected void onDigitsCleared() { - if (mOnInputChangeListener != null) { - mOnInputChangeListener.onDigitsCleared(); - } - } - - /** - * Inserts as many of the digits in the given sequence - * into the input as possible. At the end, if any digits - * were inserted, this calls {@link #onDigitInserted(String)} - * with the String value of those digits. - */ - protected final void insertDigits(int... digits) { - if (digits == null) - return; - String newDigits = ""; - for (int d : digits) { - if (mCount == mInput.length) - break; - if (d == UNMODIFIED) - continue; - mInput[mCount++] = d; - newDigits += d; - } - if (!newDigits.isEmpty()) { - // By only calling this once after making - // the insertions, we skip all of the - // intermediate callbacks. - onDigitInserted(newDigits); - } - } - - @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 = view.getText().toString(); - insertDigits(Integer.parseInt(textNum)); - } - } - - private void init() { - setAlignmentMode(ALIGN_BOUNDS); - setColumnCount(COLUMNS); - View.inflate(getContext(), contentLayout(), this); - ButterKnife.bind(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/timepickers/GridSelectorLayout.java b/app/src/main/java/com/philliphsu/clock2/timepickers/GridSelectorLayout.java deleted file mode 100644 index 55d0773..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/GridSelectorLayout.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.os.Handler; -import android.util.AttributeSet; -import android.util.Log; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.widget.ViewAnimator; - -/** - * Created by Phillip Hsu on 8/17/2016. - * - * A derivative of the AOSP datetimepicker RadialPickerLayout class. - * The animations used here are taken from the DatePickerDialog class. - * - * A ViewAnimator is a subclass of FrameLayout. - */ -public class GridSelectorLayout extends ViewAnimator implements NumbersGrid.OnNumberSelectedListener { - private static final String TAG = "GridSelectorLayout"; - - // Delay before auto-advancing the page, in ms. - // TODO: If we animate the page change, then we don't need this delay. This was - // my own logic, not ported from AOSP timepicker. - public static final int ADVANCE_PAGE_DELAY = 150; - - private static final int ANIMATION_DURATION = 300; - - private static final int HOUR_INDEX = NumberGridTimePickerDialog.HOUR_INDEX; - private static final int MINUTE_INDEX = NumberGridTimePickerDialog.MINUTE_INDEX; - // TODO: Rename to HALF_DAY_INDEX? - private static final int AMPM_INDEX = NumberGridTimePickerDialog.AMPM_INDEX; - private static final int HALF_DAY_1 = NumberGridTimePickerDialog.HALF_DAY_1; - private static final int HALF_DAY_2 = NumberGridTimePickerDialog.HALF_DAY_2; - - private OnValueSelectedListener mListener; - private boolean mTimeInitialized; - private int mCurrentHoursOfDay; - private int mCurrentMinutes; - private boolean mIs24HourMode; - private int mCurrentItemShowing; - - private HoursGrid mHoursGrid = null; - private TwentyFourHoursGrid m24HoursGrid = null; - private MinutesGrid mMinutesGrid; - private final Handler mHandler = new Handler(); - - private final Animation mInAnimation; - private final Animation mOutAnimation; - - public interface OnValueSelectedListener { - void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance); - } - - public GridSelectorLayout(Context context, AttributeSet attrs) { - super(context, attrs); - // Taken from AOSP DatePickerDialog class - // TODO: They look terrible on our views. Create new animations. - mInAnimation = new AlphaAnimation(0.0f, 1.0f); - mInAnimation.setDuration(ANIMATION_DURATION); - mOutAnimation = new AlphaAnimation(1.0f, 0.0f); - mOutAnimation.setDuration(ANIMATION_DURATION); - } - - // TODO: Why do we need a Context param? RadialPickerLayout does it too. - public void initialize(Context context, int initialHoursOfDay, int initialMinutes, boolean is24HourMode) { - if (mTimeInitialized) { - Log.e(TAG, "Time has already been initialized."); - return; - } - - // ***************************************************************************************** - // * TODO: Should we move this block to the Dialog class? This is pretty similar - // * to what AOSP's DatePickerDialog class does. I don't immediately see any - // * code that REALLY needs to be done in this class instead. - mIs24HourMode = is24HourMode; - if (is24HourMode) { - m24HoursGrid = new TwentyFourHoursGrid(context); - m24HoursGrid.initialize(this/*OnNumberSelectedListener*/); - if (initialHoursOfDay >= 12) { - // 24 hour grid is always initialized with 00-11 in the primary position - m24HoursGrid.swapTexts(); - } - addView(m24HoursGrid); - } else { - mHoursGrid = new HoursGrid(context); - mHoursGrid.initialize(this/*OnNumberSelectedListener*/); - addView(mHoursGrid); - } - mMinutesGrid = new MinutesGrid(context); - mMinutesGrid.initialize(this/*OnNumberSelectedListener*/); - addView(mMinutesGrid); - - setInAnimation(mInAnimation); - setOutAnimation(mOutAnimation); - - // ***************************************************************************************** - - // Initialize the currently-selected hour and minute. - setValueForItem(HOUR_INDEX, initialHoursOfDay); - setValueForItem(MINUTE_INDEX, initialMinutes); - - mTimeInitialized = true; - } - - void setTheme(Context context, boolean themeDark) { - // TODO: This logic needs to be in the Dialog class, since the am/pm view is contained there. -// mAmPmView.setTheme(context, themeDark); - - if (m24HoursGrid != null) { - m24HoursGrid.setTheme(context, themeDark); - } else if (mHoursGrid != null) { - mHoursGrid.setTheme(context, themeDark); - } - mMinutesGrid.setTheme(context, themeDark); - } - - public void setTime(int hours, int minutes) { - setValueForItem(HOUR_INDEX, hours); - setValueForItem(MINUTE_INDEX, minutes); - } - - public void setOnValueSelectedListener(OnValueSelectedListener listener) { - mListener = listener; - } - - /** - * Get the item (hours or minutes) that is currently showing. - */ - public int getCurrentItemShowing() { - if (mCurrentItemShowing != HOUR_INDEX && mCurrentItemShowing != MINUTE_INDEX) { - Log.e(TAG, "Current item showing was unfortunately set to "+mCurrentItemShowing); - return -1; - } - return mCurrentItemShowing; - } - - /** - * Set either minutes or hours as showing. - * @param animate True to animate the transition, false to show with no animation. - */ - public void setCurrentItemShowing(int index, boolean animate) { - if (index != HOUR_INDEX && index != MINUTE_INDEX) { - Log.e(TAG, "TimePicker does not support view at index "+index); - return; - } - - int lastIndex = getCurrentItemShowing(); - mCurrentItemShowing = index; - - if (index != lastIndex) { - setInAnimation(animate? mInAnimation : null); - setOutAnimation(animate? mOutAnimation : null); - setDisplayedChild(index); - } - } - - // TODO: The Dialog should be telling us that AM/PM was selected, via setAmOrPm(). -// @Override -// public void onAmPmSelected(int amOrPm) { -// setValueForItem(AMPM_INDEX, amOrPm); -// mListener.onValueSelected(AMPM_INDEX, amOrPm, false); -// } - - @Override - public void onNumberSelected(int number) { - // Flag set to true if this onNumberSelected() event was caused by - // long clicking in a TwentyFourHoursGrid. - boolean fakeHourItemShowing = false; - - if (getCurrentItemShowing() == HOUR_INDEX) { - if (!mIs24HourMode) { - // Change the value before passing it through the callback - int amOrPm = getIsCurrentlyAmOrPm(); - if (amOrPm == HALF_DAY_1 && number == 12) { - number = 0; - } else if (amOrPm == HALF_DAY_2 && number != 12) { - number += 12; - } - } else { - // Check if we would be changing half-days with the new value. - // This can happen if this selection occurred with a long click. - if (mCurrentHoursOfDay < 12 && number >= 12 || mCurrentHoursOfDay >= 12 && number < 12) { - int newHalfDay = getIsCurrentlyAmOrPm() == HALF_DAY_1 ? HALF_DAY_2 : HALF_DAY_1; - // Update the half-day toggles states - mListener.onValueSelected(AMPM_INDEX, newHalfDay, false); - // Advance the index prematurely to bypass the animation that would otherwise - // be forced on us if we let the listener autoAdvance us. - setCurrentItemShowing(MINUTE_INDEX, false/*animate?*/); - // We need to "trick" the listener to think we're still on HOUR_INDEX. - // When the listener gets the onValueSelected() callback, - // it needs to call our setCurrentItemShowing() with MINUTE_INDEX a second time, - // so it ends up doing nothing. (Recall that the new index must be different from the - // last index for setCurrentItemShowing() to actually change the current item - // showing.) This has the effect of "tricking" the listener to update its - // own states relevant to the HOUR_INDEX, without having it actually autoAdvance - // and forcing an animation on us. - fakeHourItemShowing = true; - } - } - } - - final int currentItemShowing = fakeHourItemShowing? HOUR_INDEX : getCurrentItemShowing(); - - setValueForItem(currentItemShowing, number); - mListener.onValueSelected(currentItemShowing, number, - true/*autoAdvance, not considered for MINUTE_INDEX*/); - } - - public int getHours() { - return mCurrentHoursOfDay; - } - - public int getMinutes() { - return mCurrentMinutes; - } - - /** - * If the hours are showing, return the current hour. If the minutes are showing, return the - * current minute. - */ - private int getCurrentlyShowingValue() { - int currentIndex = getCurrentItemShowing(); - if (currentIndex == HOUR_INDEX) { - return mCurrentHoursOfDay; - } else if (currentIndex == MINUTE_INDEX) { - return mCurrentMinutes; - } else { - return -1; - } - } - - public int getIsCurrentlyAmOrPm() { - if (mCurrentHoursOfDay < 12) { - return HALF_DAY_1; - } else if (mCurrentHoursOfDay < 24) { - return HALF_DAY_2; - } - return -1; - } - - /** - * Set the internal as either AM or PM. - */ - // TODO: Rename to setHalfDay - public void setAmOrPm(int amOrPm) { - final int initialHalfDay = getIsCurrentlyAmOrPm(); - setValueForItem(AMPM_INDEX, amOrPm); - if (amOrPm != initialHalfDay - && mIs24HourMode -// && getCurrentItemShowing() == HOUR_INDEX - && m24HoursGrid != null) { - m24HoursGrid.swapTexts(); - mListener.onValueSelected(HOUR_INDEX, mCurrentHoursOfDay, false); - } - } - - /** - * Set the internal value for the hour, minute, or AM/PM. - */ - private void setValueForItem(int index, int value) { -// Log.d(TAG, String.format("setValueForItem(%d, %d)", index, value)); - if (index == HOUR_INDEX) { - mCurrentHoursOfDay = value; - setHourGridSelection(value); - } else if (index == MINUTE_INDEX){ - mCurrentMinutes = value; - mMinutesGrid.setSelection(value); - } else if (index == AMPM_INDEX) { - if (value == HALF_DAY_1) { - mCurrentHoursOfDay = mCurrentHoursOfDay % 12; - } else if (value == HALF_DAY_2) { - mCurrentHoursOfDay = (mCurrentHoursOfDay % 12) + 12; - } - setHourGridSelection(mCurrentHoursOfDay); - } - } - - private void setHourGridSelection(int value) { - if (mIs24HourMode) { - m24HoursGrid.setSelection(value); - } else { - value = value % 12; - if (value == 0) { - value = 12; - } - mHoursGrid.setSelection(value); - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/HoursGrid.java b/app/src/main/java/com/philliphsu/clock2/timepickers/HoursGrid.java deleted file mode 100644 index 49fd939..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/HoursGrid.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; - -import com.philliphsu.clock2.R; - -/** - * Created by Phillip Hsu on 8/17/2016. - */ -public class HoursGrid extends NumbersGrid { - - public HoursGrid(Context context) { - super(context); - } - - @Override - public void setSelection(int value) { - super.setSelection(value); - // We expect value to be within [1, 12]. The position in the grid where - // value is located is thus (value - 1). - setIndicator(getChildAt(value - 1)); - } - - @Override - protected int contentLayout() { - return R.layout.content_hours_grid; - } - - @Override - protected int indexOfDefaultValue() { - // This is the index of number 12. - return getChildCount() - 1; - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/MinutesGrid.java b/app/src/main/java/com/philliphsu/clock2/timepickers/MinutesGrid.java deleted file mode 100644 index 1bd3324..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/MinutesGrid.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.view.View; -import android.widget.ImageButton; - -import com.philliphsu.clock2.R; - -/** - * Created by Phillip Hsu on 8/17/2016. - */ -public class MinutesGrid extends NumbersGrid { - private static final String TAG = "MinutesGrid"; - - private final ImageButton mMinusButton; - private final ImageButton mPlusButton; - - public MinutesGrid(Context context) { - super(context); - mMinusButton = (ImageButton) getChildAt(getChildCount() - 2); - mPlusButton = (ImageButton) getChildAt(getChildCount() - 1); - // We're not doing method binding because we don't have IDs set on these buttons. - mMinusButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - int value = getSelection() - 1; - if (value < 0) - value = 59; - setSelection(value); - mSelectionListener.onNumberSelected(value); - } - }); - mPlusButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - int value = getSelection() + 1; - if (value == 60) - value = 0; - setSelection(value); - mSelectionListener.onNumberSelected(value); - } - }); - } - - @Override - public void setSelection(int value) { - super.setSelection(value); - if (value % 5 == 0) { - // The new value is one of the predetermined minute values - int positionOfValue = value / 5; - setIndicator(getChildAt(positionOfValue)); - } else { - clearIndicator(); - } - } - - @Override - protected int contentLayout() { - return R.layout.content_minutes_grid; - } - - @Override - void setTheme(Context context, boolean themeDark) { - super.setTheme(context, themeDark); - if (themeDark) { - // Resources default to dark-themed color (#FFFFFF) - // If vector fill color is transparent, programmatically tinting will not work. - // Since dark-themed active icon color is fully opaque, use that color as the - // base color and tint at runtime as needed. - mMinusButton.setImageResource(R.drawable.ic_minus_circle_24dp); - mPlusButton.setImageResource(R.drawable.ic_add_circle_24dp); - } else { - // Tint drawables - final int colorActiveLight = ContextCompat.getColor(context, R.color.icon_color_active_light); - mMinusButton.setImageDrawable(Utils.getTintedDrawable( - context, R.drawable.ic_minus_circle_24dp, colorActiveLight)); - mPlusButton.setImageDrawable(Utils.getTintedDrawable( - context, R.drawable.ic_add_circle_24dp, colorActiveLight)); - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/NumberGridTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/timepickers/NumberGridTimePickerDialog.java deleted file mode 100644 index d4d121e..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/NumberGridTimePickerDialog.java +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.philliphsu.clock2.timepickers; - -import android.animation.ObjectAnimator; -import android.app.ActionBar.LayoutParams; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.content.ContextCompat; -import android.util.Log; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.philliphsu.clock2.R; -import com.philliphsu.clock2.timepickers.GridSelectorLayout.OnValueSelectedListener; - -import java.text.DateFormatSymbols; -import java.util.ArrayList; -import java.util.Locale; - -import butterknife.Bind; -import butterknife.OnClick; - -import static com.philliphsu.clock2.util.ConfigurationUtils.getOrientation; - -//import com.android.datetimepicker.HapticFeedbackController; -//import com.android.datetimepicker.R; -//import com.android.datetimepicker.Utils; -//import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListener; - -/** - * Dialog to set a time. - * - * A derivative of the AOSP datetimepicker TimePickerDialog class. - * - * TODO: Rename to GridSelectorTimePickerDialog - */ -public class NumberGridTimePickerDialog extends BaseTimePickerDialog implements OnValueSelectedListener { - private static final String TAG = "TimePickerDialog"; - - private static final String KEY_HOUR_OF_DAY = "hour_of_day"; - private static final String KEY_MINUTE = "minute"; - private static final String KEY_IS_24_HOUR_VIEW = "is_24_hour_view"; - private static final String KEY_CURRENT_ITEM_SHOWING = "current_item_showing"; - private static final String KEY_IN_KB_MODE = "in_kb_mode"; - private static final String KEY_TYPED_TIMES = "typed_times"; - private static final String KEY_DARK_THEME = "dark_theme"; - private static final String KEY_THEME_SET_AT_RUNTIME = "theme_set_at_runtime"; - - public static final int HOUR_INDEX = 0; - public static final int MINUTE_INDEX = 1; - // NOT a real index for the purpose of what's showing. - // TODO: Rename to HALF_DAY_INDEX? - public static final int AMPM_INDEX = 2; - // Also NOT a real index, just used for keyboard mode. - public static final int ENABLE_PICKER_INDEX = 3; - /** - * TODO: Use HALF_DAY_1 instead - */ - public static final int AM = 0; - /** - * TODO: Use HALF_DAY_2 instead - */ - public static final int PM = 1; - - // Delay before starting the pulse animation, in ms. - private static final int PULSE_ANIMATOR_DELAY = 300; -// private OnTimeSetListener mCallback; - -// private HapticFeedbackController mHapticFeedbackController; - -//TODO: Delete private TextView mDoneButton; - private TextView mHourView; - private TextView mHourSpaceView; - private TextView mMinuteView; - private TextView mMinuteSpaceView; - private TextView mAmPmTextView; - private View mAmPmHitspace; - private GridSelectorLayout mTimePicker; - - private int mSelectedColor; - private int mUnselectedColor; - private String mAmText; - private String mPmText; - - private boolean mAllowAutoAdvance; - private int mInitialHourOfDay; - private int mInitialMinute; - private boolean mIs24HourMode; - private boolean mThemeDark; - private boolean mThemeSetAtRuntime; - - // For hardware IME input. - private char mPlaceholderText; - private String mDoublePlaceholderText; - private String mDeletedKeyFormat; - private boolean mInKbMode; - private ArrayList mTypedTimes; - private Node mLegalTimesTree; - private int mAmKeyCode; - private int mPmKeyCode; - - // Accessibility strings. - private String mHourPickerDescription; - private String mSelectHours; - private String mMinutePickerDescription; - private String mSelectMinutes; - - private int mHalfDayToggleSelectedColor; - private int mHalfDayToggleUnselectedColor; - - // ====================================== MY STUFF ============================================= - - // Describes both AM/PM in the 12-hour clock and the half-days of the 24-hour clock. - // TODO: Use the *values* of AM and PM instead. - public static final int HALF_DAY_1 = AM; - public static final int HALF_DAY_2 = PM; - - // TODO: Consider moving these to GridSelectorLayout? - // TODO: Consider using findViewById() instead. This could be useful if you plan on - // releasing a library with this timepicker, because then we have no dependence on - // other third party libraries. - @Bind(R.id.fab) FloatingActionButton mDoneButton; - // These are currently defined as Buttons in the dialog's layout, - // but we refer to them as TextViews to save an extra refactoring - // step in case we change them. - @Bind(R.id.half_day_toggle_1) FrameLayout mLeftHalfDayToggle; - @Bind(R.id.half_day_toggle_2) FrameLayout mRightHalfDayToggle; - - @Override - protected int contentLayout() { - return R.layout.dialog_time_picker_number_grid; - } - - @OnClick({ R.id.half_day_toggle_1, R.id.half_day_toggle_2 }) - void onHalfDayToggleClick(View v) { - final int halfDay = v == mLeftHalfDayToggle ? HALF_DAY_1 : HALF_DAY_2; - if (halfDay != mTimePicker.getIsCurrentlyAmOrPm()) { -// if (currentHalfDay == HALF_DAY_1) { -// currentHalfDay = HALF_DAY_2; -// } else if (currentHalfDay == HALF_DAY_2) { -// currentHalfDay = HALF_DAY_1; -// } - updateHalfDay(halfDay); - mTimePicker.setAmOrPm(halfDay); - } - } - -// private void toggleHalfDay() { -// int amOrPm = mTimePicker.getIsCurrentlyAmOrPm(); -// // TODO: Use HALF_DAY_1 and 2 instead -// if (amOrPm == AM) { -// amOrPm = PM; -// } else if (amOrPm == PM){ -// amOrPm = AM; -// } -// updateAmPmDisplay(amOrPm); -// mTimePicker.setAmOrPm(amOrPm); -// // TODO: Pretty sure we don't need this -//// mSelectedHalfDay = amOrPm; -// -// // TODO: Verify the corresponding TwentyFourHourGridItem retains its indicator -// // TODO: Don't use mSelectedHourOfDay anymore. Use mTimePicker.getHours()? -// if (amOrPm == HALF_DAY_1) { -// mSelectedHourOfDay %= 12; -// } else if (amOrPm == HALF_DAY_2) { -// mSelectedHourOfDay = (mSelectedHourOfDay % 12) + 12; -// } -// // Updates the header display -// onValueSelected(HOUR_INDEX, mSelectedHourOfDay, false); -// } - -// private final OnClickListener mOnNumberClickListener = new OnClickListener() { -// @Override -// public void onClick(View v) { -// String number; -// if (v instanceof TextView) { -// number = ((TextView) v).getText().toString(); -// } else if (v instanceof TwentyFourHourGridItem) { -// number = ((TwentyFourHourGridItem) v).getPrimaryText().toString(); -// } else { -// Log.e(TAG, "TimePicker does not support button type " + v.getClass().getName()); -// return; -// } -// int value = Integer.parseInt(number); -// if (mCurrentIndex == HOUR_INDEX && !mIs24HourMode) { -// if (value == 12 && mSelectedHalfDay == HALF_DAY_1) { -// value = 0; -// } else if (value != 12 && mSelectedHalfDay == HALF_DAY_2) { -// value += 12; -// } -// } -// onValueSelected(mCurrentIndex, value, true); -// } -// }; - - // ============================================================================================= - - public NumberGridTimePickerDialog() { - // Empty constructor required for dialog fragment. - } - -// public NumberGridTimePickerDialog(Context context, int theme, OnTimeSetListener callback, -// int hourOfDay, int minute, boolean is24HourMode) { -// // Empty constructor required for dialog fragment. -// } - - public static NumberGridTimePickerDialog newInstance(OnTimeSetListener callback, - int hourOfDay, int minute, boolean is24HourMode) { - NumberGridTimePickerDialog ret = new NumberGridTimePickerDialog(); - ret.initialize(callback, hourOfDay, minute, is24HourMode); - return ret; - } - - public void initialize(OnTimeSetListener callback, - int hourOfDay, int minute, boolean is24HourMode) { - setOnTimeSetListener(callback); - mInitialHourOfDay = hourOfDay; - mInitialMinute = minute; - mIs24HourMode = is24HourMode; - mInKbMode = false; - mThemeDark = false; - mThemeSetAtRuntime = false; - } - - /** - * Set a dark or light theme. NOTE: this will only take effect for the next onCreateView. - */ - public void setThemeDark(boolean dark) { - mThemeDark = dark; - mThemeSetAtRuntime = true; - } - - public boolean isThemeDark() { - return mThemeDark; - } - - // Defined as final in our base class. -// public void setOnTimeSetListener(OnTimeSetListener callback) { -// mCallback = callback; -// } - - public void setStartTime(int hourOfDay, int minute) { - mInitialHourOfDay = hourOfDay; - mInitialMinute = minute; - mInKbMode = false; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null && savedInstanceState.containsKey(KEY_HOUR_OF_DAY) - && savedInstanceState.containsKey(KEY_MINUTE) - && savedInstanceState.containsKey(KEY_IS_24_HOUR_VIEW)) { - mInitialHourOfDay = savedInstanceState.getInt(KEY_HOUR_OF_DAY); - mInitialMinute = savedInstanceState.getInt(KEY_MINUTE); - mIs24HourMode = savedInstanceState.getBoolean(KEY_IS_24_HOUR_VIEW); - mInKbMode = savedInstanceState.getBoolean(KEY_IN_KB_MODE); - mThemeDark = savedInstanceState.getBoolean(KEY_DARK_THEME); - mThemeSetAtRuntime = savedInstanceState.getBoolean(KEY_THEME_SET_AT_RUNTIME); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { -// getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - -// View view = inflater.inflate(R.layout.time_picker_dialog, null); -// KeyboardListener keyboardListener = new KeyboardListener(); -// view.findViewById(R.id.time_picker_dialog).setOnKeyListener(keyboardListener); - - View view = super.onCreateView(inflater, container, savedInstanceState); - - if (!mThemeSetAtRuntime) { - mThemeDark = Utils.isDarkTheme(getActivity(), mThemeDark); - } - - Resources res = getResources(); - mHourPickerDescription = res.getString(R.string.hour_picker_description); - mSelectHours = res.getString(R.string.select_hours); - mMinutePickerDescription = res.getString(R.string.minute_picker_description); - mSelectMinutes = res.getString(R.string.select_minutes); -// mSelectedColor = res.getColor(mThemeDark? R.color.red : R.color.blue); -// mUnselectedColor = -// res.getColor(mThemeDark? android.R.color.white : R.color.numbers_text_color); - mSelectedColor = res.getColor(android.R.color.white); - mUnselectedColor = res.getColor(R.color.unselected_color); - - mHourView = (TextView) view.findViewById(R.id.hours); -// mHourView.setOnKeyListener(keyboardListener); - mHourSpaceView = (TextView) view.findViewById(R.id.hour_space); - mMinuteSpaceView = (TextView) view.findViewById(R.id.minutes_space); - mMinuteView = (TextView) view.findViewById(R.id.minutes); -// mMinuteView.setOnKeyListener(keyboardListener); - mAmPmTextView = (TextView) view.findViewById(R.id.ampm_label); -// mAmPmTextView.setOnKeyListener(keyboardListener); - String[] amPmTexts = new DateFormatSymbols().getAmPmStrings(); - mAmText = amPmTexts[0]; - mPmText = amPmTexts[1]; - - TextView tv1 = (TextView) mLeftHalfDayToggle.getChildAt(0); - TextView tv2 = (TextView) mRightHalfDayToggle.getChildAt(0); - if (mIs24HourMode) { - final int iconPadding = getResources().getDimensionPixelSize(R.dimen.half_day_icon_padding); - tv1.setText("00 - 11"); - tv2.setText("12 - 23"); - // We need different drawable resources for each case, not a single one that we - // just tint differently, because the orientation of each one is different. - final int icon1 = mThemeDark? R.drawable.ic_half_day_1_dark_24dp : R.drawable.ic_half_day_1_24dp; - final int icon2 = mThemeDark? R.drawable.ic_half_day_2_dark_24dp : R.drawable.ic_half_day_2_24dp; - // Determine the direction the icons should be in - int left1 = 0, left2 = 0, top1 = 0, top2 = 0; - switch (getOrientation(getResources())) { - case Configuration.ORIENTATION_PORTRAIT: - left1 = icon1; - left2 = icon2; - top1 = top2 = 0; - break; - case Configuration.ORIENTATION_LANDSCAPE: - left1 = left2 = 0; - top1 = icon1; - top2 = icon2; - break; - } - // Intrinsic bounds means the drawable's own bounds? So 24dp box. - tv1.setCompoundDrawablesWithIntrinsicBounds(left1, top1, 0, 0); - tv2.setCompoundDrawablesWithIntrinsicBounds(left2, top2, 0, 0); - tv1.setCompoundDrawablePadding(iconPadding); - tv2.setCompoundDrawablePadding(iconPadding); - } else { - tv1.setText(mAmText); - tv2.setText(mPmText); - } - -// mHapticFeedbackController = new HapticFeedbackController(getActivity()); - mTimePicker = (GridSelectorLayout) view.findViewById(R.id.time_picker); - mTimePicker.setOnValueSelectedListener(this); -// mTimePicker.setOnKeyListener(keyboardListener); - mTimePicker.initialize(getActivity(), /*mHapticFeedbackController,*/ mInitialHourOfDay, - mInitialMinute, mIs24HourMode); - - int currentItemShowing = HOUR_INDEX; - if (savedInstanceState != null && - savedInstanceState.containsKey(KEY_CURRENT_ITEM_SHOWING)) { - currentItemShowing = savedInstanceState.getInt(KEY_CURRENT_ITEM_SHOWING); - } - setCurrentItemShowing(currentItemShowing, false, true, true); - mTimePicker.invalidate(); - - mHourView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - setCurrentItemShowing(HOUR_INDEX, true, false, true); - tryVibrate(); - } - }); - mMinuteView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - setCurrentItemShowing(MINUTE_INDEX, true, false, true); - tryVibrate(); - } - }); - -// mDoneButton = (TextView) view.findViewById(R.id.done_button); - // This is our FAB. - mDoneButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mInKbMode && isTypedTimeFullyLegal()) { // TODO: Delete -// finishKbMode(false); - } else { - tryVibrate(); - } - onTimeSet(mTimePicker, mTimePicker.getHours(), mTimePicker.getMinutes()); - } - }); -// mDoneButton.setOnKeyListener(keyboardListener); - - // Enable or disable the AM/PM view. - mAmPmHitspace = view.findViewById(R.id.ampm_hitspace); - if (mIs24HourMode) { - mAmPmTextView.setVisibility(View.GONE); - - RelativeLayout.LayoutParams paramsSeparator = new RelativeLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - paramsSeparator.addRule(RelativeLayout.CENTER_IN_PARENT); - TextView separatorView = (TextView) view.findViewById(R.id.separator); - separatorView.setLayoutParams(paramsSeparator); - } else { - mAmPmTextView.setVisibility(View.VISIBLE); - mAmPmHitspace.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - tryVibrate(); - int amOrPm = mTimePicker.getIsCurrentlyAmOrPm(); - if (amOrPm == AM) { - amOrPm = PM; - } else if (amOrPm == PM){ - amOrPm = AM; - } - updateHalfDay(amOrPm); - mTimePicker.setAmOrPm(amOrPm); - } - }); - } - - mAllowAutoAdvance = true; - setHour(mInitialHourOfDay, true); - setMinute(mInitialMinute); - - // Set up for keyboard mode. - mDoublePlaceholderText = res.getString(R.string.time_placeholder); - mDeletedKeyFormat = res.getString(R.string.deleted_key); - mPlaceholderText = mDoublePlaceholderText.charAt(0); - mAmKeyCode = mPmKeyCode = -1; - generateLegalTimesTree(); - if (mInKbMode) { - mTypedTimes = savedInstanceState.getIntegerArrayList(KEY_TYPED_TIMES); -// tryStartingKbMode(-1); - mHourView.invalidate(); - } else if (mTypedTimes == null) { - mTypedTimes = new ArrayList(); - } - - // Set the theme at the end so that the initialize()s above don't counteract the theme. - mTimePicker.setTheme(getActivity().getApplicationContext(), mThemeDark); - // Prepare some colors to use. - int white = res.getColor(android.R.color.white); - int circleBackground = res.getColor(R.color.circle_background); - int line = res.getColor(R.color.line_background); - int timeDisplay = res.getColor(R.color.numbers_text_color); - // TODO: Port the AOSP timepicker files that contain these resources. -// ColorStateList doneTextColor = res.getColorStateList(R.color.done_text_color); -// int doneBackground = R.drawable.done_background_color; - - int darkGray = res.getColor(R.color.dark_gray); - int lightGray = res.getColor(R.color.light_gray); - int darkLine = res.getColor(R.color.line_dark); - // TODO: Port the AOSP timepicker files that contain these resources. -// ColorStateList darkDoneTextColor = res.getColorStateList(R.color.done_text_color_dark); -// int darkDoneBackground = R.drawable.done_background_color_dark; - - // My colors - int accentColor = Utils.getThemeAccentColor(getContext()); - - // Set the colors for each view based on the theme. - view.setBackgroundColor(mThemeDark? darkGray : white); - view.findViewById(R.id.time_display_background).setBackgroundColor(mThemeDark? lightGray : accentColor); - view.findViewById(R.id.time_display).setBackgroundColor(mThemeDark? lightGray : accentColor); - ((TextView) view.findViewById(R.id.separator)).setTextColor(/*mThemeDark? white : timeDisplay*/mUnselectedColor); - ((TextView) view.findViewById(R.id.ampm_label)).setTextColor(/*mThemeDark? white : timeDisplay*/mUnselectedColor); -// view.findViewById(R.id.line).setBackgroundColor(mThemeDark? darkLine : line); - view.findViewById(R.id.divider).setBackgroundColor(mThemeDark? darkLine : line); -// mDoneButton.setTextColor(mThemeDark? darkDoneTextColor : doneTextColor); - // The AOSP timepicker originally uses these colors for the CircleView - // We already set the correct background color of the entire view tree. -// mTimePicker.setBackgroundColor(mThemeDark? /*lightGray : circleBackground*/ darkGray : white); -// mDoneButton.setBackgroundResource(mThemeDark? darkDoneBackground : doneBackground); - - // Set the color on the FAB - // http://stackoverflow.com/a/32031019/5055032 - // ***************************************************************************************** - // NOTE: IF YOU DECIDE TO MOVE THE FAB AND THE HALF DAY TOGGLES TO THE GRIDSELECTORLAYOUT - // CLASS, YOU SHOULD CHANGE THE CONTEXT PASSED TO GridSelectorLayout#setTheme() FROM THE - // APPLICATION CONTEXT TO THE LOCAL CONTEXT. OTHERWISE, IT WOULD NOT BE ABLE TO RETRIEVE - // THE CORRECT ACCENT COLOR. WE ALREADY FACED THIS ISSUE WITH THE NUMPADTIMEPICKERDIALOG. - // DO A CTRL+F FOR mTimePicker.setTheme FOR THE CODE IN DISCUSSION. - // ***************************************************************************************** - // Color in normal state - mDoneButton.setBackgroundTintList(ColorStateList.valueOf(accentColor)); - // Color in pressed state. A ripple expands outwards from the point of contact throughout - // the fab when it is long pressed. -// mDoneButton.setRippleColor(/*your color here*/); - - // Set the color on the half-day toggles - // We already set the correct background color of the entire view tree. -// view.findViewById(R.id.half_day_toggles).setBackgroundColor(mThemeDark? /*lightGray : circleBackground*/ darkGray : white); - mHalfDayToggleSelectedColor = accentColor; -// mHalfDayToggleUnselectedColor = Utils.getTextColorFromThemeAttr(getContext(), -// // The colors are in the correct order, which happens to be the reverse of the order -// // used in the NumbersGrids... -// mThemeDark? android.R.attr.textColorPrimaryInverse : android.R.attr.textColorPrimary); - mHalfDayToggleUnselectedColor = ContextCompat.getColor(getContext(), - mThemeDark? R.color.text_color_primary_dark : R.color.text_color_primary_light); - - Utils.setColorControlHighlight(mLeftHalfDayToggle, accentColor); - Utils.setColorControlHighlight(mRightHalfDayToggle, accentColor); - - // Update the half day at the end when the state colors have been initialized - updateHalfDay(mInitialHourOfDay < 12? AM : PM); - return view; - } - - @Override - public void onResume() { - super.onResume(); -// mHapticFeedbackController.start(); - } - - @Override - public void onPause() { - super.onPause(); -// mHapticFeedbackController.stop(); - } - - public void tryVibrate() { -// mHapticFeedbackController.tryVibrate(); - } - - private void updateHalfDay(int halfDay) { - updateAmPmDisplay(halfDay); - updateHalfDayTogglesState(halfDay); - } - - private void updateAmPmDisplay(int amOrPm) { - if (amOrPm == AM) { - mAmPmTextView.setText(mAmText); - Utils.tryAccessibilityAnnounce(mTimePicker, mAmText); - mAmPmHitspace.setContentDescription(mAmText); - } else if (amOrPm == PM){ - mAmPmTextView.setText(mPmText); - Utils.tryAccessibilityAnnounce(mTimePicker, mPmText); - mAmPmHitspace.setContentDescription(mPmText); - } else { - mAmPmTextView.setText(mDoublePlaceholderText); - } - } - - /** - * Update the indicator of the toggle buttons to show the given half-day as selected. - * @param halfDay the half-day that should be shown as selected - */ - private void updateHalfDayTogglesState(int halfDay) { - TextView leftHalfDayToggle = (TextView) mLeftHalfDayToggle.getChildAt(0); - TextView rightHalfDayToggle = (TextView) mRightHalfDayToggle.getChildAt(0); - switch (halfDay) { - case HALF_DAY_1: - leftHalfDayToggle.setTextColor(mHalfDayToggleSelectedColor); - rightHalfDayToggle.setTextColor(mHalfDayToggleUnselectedColor); - break; - case HALF_DAY_2: - rightHalfDayToggle.setTextColor(mHalfDayToggleSelectedColor); - leftHalfDayToggle.setTextColor(mHalfDayToggleUnselectedColor); - break; - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - if (mTimePicker != null) { - outState.putInt(KEY_HOUR_OF_DAY, mTimePicker.getHours()); - outState.putInt(KEY_MINUTE, mTimePicker.getMinutes()); - outState.putBoolean(KEY_IS_24_HOUR_VIEW, mIs24HourMode); - outState.putInt(KEY_CURRENT_ITEM_SHOWING, mTimePicker.getCurrentItemShowing()); - outState.putBoolean(KEY_IN_KB_MODE, mInKbMode); - if (mInKbMode) { - outState.putIntegerArrayList(KEY_TYPED_TIMES, mTypedTimes); - } - outState.putBoolean(KEY_DARK_THEME, mThemeDark); - outState.putBoolean(KEY_THEME_SET_AT_RUNTIME, mThemeSetAtRuntime); - } - } - - /** - * Called by the picker for updating the header display. - */ - @Override - public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) { - if (pickerIndex == HOUR_INDEX) { - setHour(newValue, false); - String announcement = String.format("%d", newValue); - if (mAllowAutoAdvance && autoAdvance) { - setCurrentItemShowing(MINUTE_INDEX, true, true, false); - announcement += ". " + mSelectMinutes; - } else { - mTimePicker.setContentDescription(mHourPickerDescription + ": " + newValue); - } - - Utils.tryAccessibilityAnnounce(mTimePicker, announcement); - } else if (pickerIndex == MINUTE_INDEX){ - setMinute(newValue); - mTimePicker.setContentDescription(mMinutePickerDescription + ": " + newValue); - } else if (pickerIndex == AMPM_INDEX) { - updateHalfDay(newValue); - } else if (pickerIndex == ENABLE_PICKER_INDEX) { - if (!isTypedTimeFullyLegal()) { - mTypedTimes.clear(); - } -// finishKbMode(true); - } - } - - private void setHour(int value, boolean announce) { - String format; - if (mIs24HourMode) { - format = "%02d"; - } else { - format = "%d"; - value = value % 12; - if (value == 0) { - value = 12; - } - } - - CharSequence text = String.format(format, value); - mHourView.setText(text); - mHourSpaceView.setText(text); - if (announce) { - Utils.tryAccessibilityAnnounce(mTimePicker, text); - } - } - - private void setMinute(int value) { - if (value == 60) { - value = 0; - } - CharSequence text = String.format(Locale.getDefault(), "%02d", value); - Utils.tryAccessibilityAnnounce(mTimePicker, text); - mMinuteView.setText(text); - mMinuteSpaceView.setText(text); - } - - // Show either Hours or Minutes. - private void setCurrentItemShowing(int index, boolean animateCircle, boolean delayLabelAnimate, - boolean announce) { - mTimePicker.setCurrentItemShowing(index, animateCircle); - - TextView labelToAnimate; - if (index == HOUR_INDEX) { - int hours = mTimePicker.getHours(); - if (!mIs24HourMode) { - hours = hours % 12; - } - mTimePicker.setContentDescription(mHourPickerDescription + ": " + hours); - if (announce) { - Utils.tryAccessibilityAnnounce(mTimePicker, mSelectHours); - } - labelToAnimate = mHourView; - } else { - int minutes = mTimePicker.getMinutes(); - mTimePicker.setContentDescription(mMinutePickerDescription + ": " + minutes); - if (announce) { - Utils.tryAccessibilityAnnounce(mTimePicker, mSelectMinutes); - } - labelToAnimate = mMinuteView; - } - - int hourColor = (index == HOUR_INDEX)? mSelectedColor : mUnselectedColor; - int minuteColor = (index == MINUTE_INDEX)? mSelectedColor : mUnselectedColor; - mHourView.setTextColor(hourColor); - mMinuteView.setTextColor(minuteColor); - - ObjectAnimator pulseAnimator = Utils.getPulseAnimator(labelToAnimate, 0.85f, 1.1f); - if (delayLabelAnimate) { - pulseAnimator.setStartDelay(PULSE_ANIMATOR_DELAY); - } - pulseAnimator.start(); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - // END RELEVANT CODE - //////////////////////////////////////////////////////////////////////////////////////////////// - -// /** -// * For keyboard mode, processes key events. -// * @param keyCode the pressed key. -// * @return true if the key was successfully processed, false otherwise. -// */ -// private boolean processKeyUp(int keyCode) { -// if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_BACK) { -// dismiss(); -// return true; -// } else if (keyCode == KeyEvent.KEYCODE_TAB) { -// if(mInKbMode) { -// if (isTypedTimeFullyLegal()) { -// finishKbMode(true); -// } -// return true; -// } -// } else if (keyCode == KeyEvent.KEYCODE_ENTER) { -// if (mInKbMode) { -// if (!isTypedTimeFullyLegal()) { -// return true; -// } -// finishKbMode(false); -// } -// if (mCallback != null) { -// mCallback.onTimeSet(mTimePicker, -// mTimePicker.getHours(), mTimePicker.getMinutes()); -// } -// dismiss(); -// return true; -// } else if (keyCode == KeyEvent.KEYCODE_DEL) { -// if (mInKbMode) { -// if (!mTypedTimes.isEmpty()) { -// int deleted = deleteLastTypedKey(); -// String deletedKeyStr; -// if (deleted == getAmOrPmKeyCode(AM)) { -// deletedKeyStr = mAmText; -// } else if (deleted == getAmOrPmKeyCode(PM)) { -// deletedKeyStr = mPmText; -// } else { -// deletedKeyStr = String.format("%d", getValFromKeyCode(deleted)); -// } -// Utils.tryAccessibilityAnnounce(mTimePicker, -// String.format(mDeletedKeyFormat, deletedKeyStr)); -// updateDisplay(true); -// } -// } -// } else if (keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1 -// || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3 -// || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5 -// || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7 -// || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9 -// || (!mIs24HourMode && -// (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) { -// if (!mInKbMode) { -// if (mTimePicker == null) { -// // Something's wrong, because time picker should definitely not be null. -// Log.e(TAG, "Unable to initiate keyboard mode, TimePicker was null."); -// return true; -// } -// mTypedTimes.clear(); -// tryStartingKbMode(keyCode); -// return true; -// } -// // We're already in keyboard mode. -// if (addKeyIfLegal(keyCode)) { -// updateDisplay(false); -// } -// return true; -// } -// return false; -// } - -// /** -// * Try to start keyboard mode with the specified key, as long as the timepicker is not in the -// * middle of a touch-event. -// * @param keyCode The key to use as the first press. Keyboard mode will not be started if the -// * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting -// * key. -// */ -// private void tryStartingKbMode(int keyCode) { -// if (mTimePicker.trySettingInputEnabled(false) && -// (keyCode == -1 || addKeyIfLegal(keyCode))) { -// mInKbMode = true; -// mDoneButton.setEnabled(false); -// updateDisplay(false); -// } -// } -// -// private boolean addKeyIfLegal(int keyCode) { -// // If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode, -// // we'll need to see if AM/PM have been typed. -// if ((mIs24HourMode && mTypedTimes.size() == 4) || -// (!mIs24HourMode && isTypedTimeFullyLegal())) { -// return false; -// } -// -// mTypedTimes.add(keyCode); -// if (!isTypedTimeLegalSoFar()) { -// deleteLastTypedKey(); -// return false; -// } -// -// int val = getValFromKeyCode(keyCode); -// Utils.tryAccessibilityAnnounce(mTimePicker, String.format("%d", val)); -// // Automatically fill in 0's if AM or PM was legally entered. -// if (isTypedTimeFullyLegal()) { -// if (!mIs24HourMode && mTypedTimes.size() <= 3) { -// mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0); -// mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0); -// } -// mDoneButton.setEnabled(true); -// } -// -// return true; -// } -// -// /** -// * Traverse the tree to see if the keys that have been typed so far are legal as is, -// * or may become legal as more keys are typed (excluding backspace). -// */ -// private boolean isTypedTimeLegalSoFar() { -// Node node = mLegalTimesTree; -// for (int keyCode : mTypedTimes) { -// node = node.canReach(keyCode); -// if (node == null) { -// return false; -// } -// } -// return true; -// } -// - /** - * Check if the time that has been typed so far is completely legal, as is. - */ - private boolean isTypedTimeFullyLegal() { - if (mIs24HourMode) { - // For 24-hour mode, the time is legal if the hours and minutes are each legal. Note: - // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode. - int[] values = getEnteredTime(null); - return (values[0] >= 0 && values[1] >= 0 && values[1] < 60); - } else { - // For AM/PM mode, the time is legal if it contains an AM or PM, as those can only be - // legally added at specific times based on the tree's algorithm. - return (mTypedTimes.contains(getAmOrPmKeyCode(AM)) || - mTypedTimes.contains(getAmOrPmKeyCode(PM))); - } - } -// -// private int deleteLastTypedKey() { -// int deleted = mTypedTimes.remove(mTypedTimes.size() - 1); -// if (!isTypedTimeFullyLegal()) { -// mDoneButton.setEnabled(false); -// } -// return deleted; -// } -// -// /** -// * Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time. -// * @param changeDisplays If true, update the displays with the relevant time. -// */ -// private void finishKbMode(boolean updateDisplays) { -// mInKbMode = false; -// if (!mTypedTimes.isEmpty()) { -// int values[] = getEnteredTime(null); -// mTimePicker.setTime(values[0], values[1]); -// if (!mIs24HourMode) { -// mTimePicker.setAmOrPm(values[2]); -// } -// mTypedTimes.clear(); -// } -// if (updateDisplays) { -// updateDisplay(false); -// mTimePicker.trySettingInputEnabled(true); -// } -// } -// -// /** -// * Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is -// * empty, either show an empty display (filled with the placeholder text), or update from the -// * timepicker's values. -// * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text. -// * Otherwise, revert to the timepicker's values. -// */ -// private void updateDisplay(boolean allowEmptyDisplay) { -// if (!allowEmptyDisplay && mTypedTimes.isEmpty()) { -// int hour = mTimePicker.getHours(); -// int minute = mTimePicker.getMinutes(); -// setHour(hour, true); -// setMinute(minute); -// if (!mIs24HourMode) { -// updateAmPmDisplay(hour < 12? AM : PM); -// } -// setCurrentItemShowing(mTimePicker.getCurrentItemShowing(), true, true, true); -// mDoneButton.setEnabled(true); -// } else { -// Boolean[] enteredZeros = {false, false}; -// int[] values = getEnteredTime(enteredZeros); -// String hourFormat = enteredZeros[0]? "%02d" : "%2d"; -// String minuteFormat = (enteredZeros[1])? "%02d" : "%2d"; -// String hourStr = (values[0] == -1)? mDoublePlaceholderText : -// String.format(hourFormat, values[0]).replace(' ', mPlaceholderText); -// String minuteStr = (values[1] == -1)? mDoublePlaceholderText : -// String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText); -// mHourView.setText(hourStr); -// mHourSpaceView.setText(hourStr); -// mHourView.setTextColor(mUnselectedColor); -// mMinuteView.setText(minuteStr); -// mMinuteSpaceView.setText(minuteStr); -// mMinuteView.setTextColor(mUnselectedColor); -// if (!mIs24HourMode) { -// updateAmPmDisplay(values[2]); -// } -// } -// } - - private static int getValFromKeyCode(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_0: - return 0; - case KeyEvent.KEYCODE_1: - return 1; - case KeyEvent.KEYCODE_2: - return 2; - case KeyEvent.KEYCODE_3: - return 3; - case KeyEvent.KEYCODE_4: - return 4; - case KeyEvent.KEYCODE_5: - return 5; - case KeyEvent.KEYCODE_6: - return 6; - case KeyEvent.KEYCODE_7: - return 7; - case KeyEvent.KEYCODE_8: - return 8; - case KeyEvent.KEYCODE_9: - return 9; - default: - return -1; - } - } - - /** - * Get the currently-entered time, as integer values of the hours and minutes typed. - * @param enteredZeros A size-2 boolean array, which the caller should initialize, and which - * may then be used for the caller to know whether zeros had been explicitly entered as either - * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's. - * @return A size-3 int array. The first value will be the hours, the second value will be the - * minutes, and the third will be either TimePickerDialog.AM or TimePickerDialog.PM. - */ - private int[] getEnteredTime(Boolean[] enteredZeros) { - int amOrPm = -1; - int startIndex = 1; - if (!mIs24HourMode && isTypedTimeFullyLegal()) { - int keyCode = mTypedTimes.get(mTypedTimes.size() - 1); - if (keyCode == getAmOrPmKeyCode(AM)) { - amOrPm = AM; - } else if (keyCode == getAmOrPmKeyCode(PM)){ - amOrPm = PM; - } - startIndex = 2; - } - int minute = -1; - int hour = -1; - for (int i = startIndex; i <= mTypedTimes.size(); i++) { - int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i)); - if (i == startIndex) { - minute = val; - } else if (i == startIndex+1) { - minute += 10*val; - if (enteredZeros != null && val == 0) { - enteredZeros[1] = true; - } - } else if (i == startIndex+2) { - hour = val; - } else if (i == startIndex+3) { - hour += 10*val; - if (enteredZeros != null && val == 0) { - enteredZeros[0] = true; - } - } - } - - int[] ret = {hour, minute, amOrPm}; - return ret; - } - - /** - * Get the keycode value for AM and PM in the current language. - */ - private int getAmOrPmKeyCode(int amOrPm) { - // Cache the codes. - if (mAmKeyCode == -1 || mPmKeyCode == -1) { - // Find the first character in the AM/PM text that is unique. - KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); - char amChar; - char pmChar; - for (int i = 0; i < Math.max(mAmText.length(), mPmText.length()); i++) { - amChar = mAmText.toLowerCase(Locale.getDefault()).charAt(i); - pmChar = mPmText.toLowerCase(Locale.getDefault()).charAt(i); - if (amChar != pmChar) { - KeyEvent[] events = kcm.getEvents(new char[]{amChar, pmChar}); - // There should be 4 events: a down and up for both AM and PM. - if (events != null && events.length == 4) { - mAmKeyCode = events[0].getKeyCode(); - mPmKeyCode = events[2].getKeyCode(); - } else { - Log.e(TAG, "Unable to find keycodes for AM and PM."); - } - break; - } - } - } - if (amOrPm == AM) { - return mAmKeyCode; - } else if (amOrPm == PM) { - return mPmKeyCode; - } - - return -1; - } - - /** - * Create a tree for deciding what keys can legally be typed. - */ - private void generateLegalTimesTree() { - // Create a quick cache of numbers to their keycodes. - int k0 = KeyEvent.KEYCODE_0; - int k1 = KeyEvent.KEYCODE_1; - int k2 = KeyEvent.KEYCODE_2; - int k3 = KeyEvent.KEYCODE_3; - int k4 = KeyEvent.KEYCODE_4; - int k5 = KeyEvent.KEYCODE_5; - int k6 = KeyEvent.KEYCODE_6; - int k7 = KeyEvent.KEYCODE_7; - int k8 = KeyEvent.KEYCODE_8; - int k9 = KeyEvent.KEYCODE_9; - - // The root of the tree doesn't contain any numbers. - mLegalTimesTree = new Node(); - if (mIs24HourMode) { - // We'll be re-using these nodes, so we'll save them. - Node minuteFirstDigit = new Node(k0, k1, k2, k3, k4, k5); - Node minuteSecondDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); - // The first digit must be followed by the second digit. - minuteFirstDigit.addChild(minuteSecondDigit); - - // The first digit may be 0-1. - Node firstDigit = new Node(k0, k1); - mLegalTimesTree.addChild(firstDigit); - - // When the first digit is 0-1, the second digit may be 0-5. - Node secondDigit = new Node(k0, k1, k2, k3, k4, k5); - firstDigit.addChild(secondDigit); - // We may now be followed by the first minute digit. E.g. 00:09, 15:58. - secondDigit.addChild(minuteFirstDigit); - - // When the first digit is 0-1, and the second digit is 0-5, the third digit may be 6-9. - Node thirdDigit = new Node(k6, k7, k8, k9); - // The time must now be finished. E.g. 0:55, 1:08. - secondDigit.addChild(thirdDigit); - - // When the first digit is 0-1, the second digit may be 6-9. - secondDigit = new Node(k6, k7, k8, k9); - firstDigit.addChild(secondDigit); - // We must now be followed by the first minute digit. E.g. 06:50, 18:20. - secondDigit.addChild(minuteFirstDigit); - - // The first digit may be 2. - firstDigit = new Node(k2); - mLegalTimesTree.addChild(firstDigit); - - // When the first digit is 2, the second digit may be 0-3. - secondDigit = new Node(k0, k1, k2, k3); - firstDigit.addChild(secondDigit); - // We must now be followed by the first minute digit. E.g. 20:50, 23:09. - secondDigit.addChild(minuteFirstDigit); - - // When the first digit is 2, the second digit may be 4-5. - secondDigit = new Node(k4, k5); - firstDigit.addChild(secondDigit); - // We must now be followd by the last minute digit. E.g. 2:40, 2:53. - secondDigit.addChild(minuteSecondDigit); - - // The first digit may be 3-9. - firstDigit = new Node(k3, k4, k5, k6, k7, k8, k9); - mLegalTimesTree.addChild(firstDigit); - // We must now be followed by the first minute digit. E.g. 3:57, 8:12. - firstDigit.addChild(minuteFirstDigit); - } else { - // We'll need to use the AM/PM node a lot. - // Set up AM and PM to respond to "a" and "p". - Node ampm = new Node(getAmOrPmKeyCode(AM), getAmOrPmKeyCode(PM)); - - // The first hour digit may be 1. - Node firstDigit = new Node(k1); - mLegalTimesTree.addChild(firstDigit); - // We'll allow quick input of on-the-hour times. E.g. 1pm. - firstDigit.addChild(ampm); - - // When the first digit is 1, the second digit may be 0-2. - Node secondDigit = new Node(k0, k1, k2); - firstDigit.addChild(secondDigit); - // Also for quick input of on-the-hour times. E.g. 10pm, 12am. - secondDigit.addChild(ampm); - - // When the first digit is 1, and the second digit is 0-2, the third digit may be 0-5. - Node thirdDigit = new Node(k0, k1, k2, k3, k4, k5); - secondDigit.addChild(thirdDigit); - // The time may be finished now. E.g. 1:02pm, 1:25am. - thirdDigit.addChild(ampm); - - // When the first digit is 1, the second digit is 0-2, and the third digit is 0-5, - // the fourth digit may be 0-9. - Node fourthDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); - thirdDigit.addChild(fourthDigit); - // The time must be finished now. E.g. 10:49am, 12:40pm. - fourthDigit.addChild(ampm); - - // When the first digit is 1, and the second digit is 0-2, the third digit may be 6-9. - thirdDigit = new Node(k6, k7, k8, k9); - secondDigit.addChild(thirdDigit); - // The time must be finished now. E.g. 1:08am, 1:26pm. - thirdDigit.addChild(ampm); - - // When the first digit is 1, the second digit may be 3-5. - secondDigit = new Node(k3, k4, k5); - firstDigit.addChild(secondDigit); - - // When the first digit is 1, and the second digit is 3-5, the third digit may be 0-9. - thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); - secondDigit.addChild(thirdDigit); - // The time must be finished now. E.g. 1:39am, 1:50pm. - thirdDigit.addChild(ampm); - - // The hour digit may be 2-9. - firstDigit = new Node(k2, k3, k4, k5, k6, k7, k8, k9); - mLegalTimesTree.addChild(firstDigit); - // We'll allow quick input of on-the-hour-times. E.g. 2am, 5pm. - firstDigit.addChild(ampm); - - // When the first digit is 2-9, the second digit may be 0-5. - secondDigit = new Node(k0, k1, k2, k3, k4, k5); - firstDigit.addChild(secondDigit); - - // When the first digit is 2-9, and the second digit is 0-5, the third digit may be 0-9. - thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); - secondDigit.addChild(thirdDigit); - // The time must be finished now. E.g. 2:57am, 9:30pm. - thirdDigit.addChild(ampm); - } - } - - /** - * Simple node class to be used for traversal to check for legal times. - * mLegalKeys represents the keys that can be typed to get to the node. - * mChildren are the children that can be reached from this node. - */ - private class Node { - private int[] mLegalKeys; - private ArrayList mChildren; - - public Node(int... legalKeys) { - mLegalKeys = legalKeys; - mChildren = new ArrayList(); - } - - public void addChild(Node child) { - mChildren.add(child); - } - - public boolean containsKey(int key) { - for (int i = 0; i < mLegalKeys.length; i++) { - if (mLegalKeys[i] == key) { - return true; - } - } - return false; - } - - public Node canReach(int key) { - if (mChildren == null) { - return null; - } - for (Node child : mChildren) { - if (child.containsKey(key)) { - return child; - } - } - return null; - } - } - -// private class KeyboardListener implements OnKeyListener { -// @Override -// public boolean onKey(View v, int keyCode, KeyEvent event) { -// if (event.getAction() == KeyEvent.ACTION_UP) { -// return processKeyUp(keyCode); -// } -// return false; -// } -// } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/NumbersGrid.java b/app/src/main/java/com/philliphsu/clock2/timepickers/NumbersGrid.java deleted file mode 100644 index b395202..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/NumbersGrid.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.support.annotation.LayoutRes; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.GridLayout; -import android.util.Log; -import android.view.View; -import android.widget.TextView; - -import com.philliphsu.clock2.R; - -/** - * Created by Phillip Hsu on 8/17/2016. - */ -public abstract class NumbersGrid extends GridLayout implements View.OnClickListener { - private static final String TAG = "NumbersGrid"; - private static final int COLUMN_COUNT = 3; - - // Package visible so our concrete subclasses (in the same package) can access this. - OnNumberSelectedListener mSelectionListener; - View mLastSelectedView; - - final int mSelectedTextColor; - // TODO: The half-day buttons in the dialog's layout also need to use this color. - // Consider moving this to either the Dialog class, or move the buttons and the FAB - // to the GridSelectorLayout class and then move these to GridSelectorLayout. - int mDefaultTextColor; - - private boolean mIsInitialized; - private int mSelection; // The number selected from this grid - - public interface OnNumberSelectedListener { - void onNumberSelected(int number); - } - - @LayoutRes - protected abstract int contentLayout(); - - public NumbersGrid(Context context) { - super(context); - setColumnCount(COLUMN_COUNT); - inflate(context, contentLayout(), this); - // We don't do method binding because we don't know the IDs of - // our subclasses' buttons, if any. - registerClickListeners(); - mIsInitialized = false; - mDefaultTextColor = ContextCompat.getColor(context, R.color.text_color_primary_light); - // The reason we can use the Context passed here and get the correct accent color - // is that this NumbersGrid is programmatically created by the GridSelectorLayout in - // its initialize(), and the Context passed in there is from - // NumberGridTimePickerDialog.getActivity(). - mSelectedTextColor = Utils.getThemeAccentColor(context); - final View defaultSelectedView = getChildAt(indexOfDefaultValue()); - mSelection = valueOf(defaultSelectedView); - setIndicator(defaultSelectedView); - } - - public void initialize(OnNumberSelectedListener listener) { - if (mIsInitialized) { - Log.e(TAG, "This NumbersGrid may only be initialized once."); - return; - } - mSelectionListener = listener; - mIsInitialized = true; - } - - public int getSelection() { - return mSelection; - } - - public void setSelection(int value) { - mSelection = value; - } - - /** - * The default implementation assumes the clicked view is of type TextView, - * casts the view accordingly, and parses the number from the text it contains. - * @param v the View that was clicked - */ - @Override - public void onClick(View v) { - setIndicator(v); - mSelection = valueOf(v); - mSelectionListener.onNumberSelected(mSelection); - } - - /** - * Returns whether the specified View from our hierarchy can have an - * OnClickListener registered on it. The default implementation - * checks if this view is of type TextView. Subclasses can override - * this to fit their own criteria of what types of Views in their - * hierarchy can have a click listener registered on. - * - * @param view a child view from our hierarchy - */ - protected boolean canRegisterClickListener(View view) { - return view instanceof TextView; - } - - /** - * Sets a selection indicator on the clicked number button. The indicator - * is the accent color applied to the button's text. - * - * @param view the clicked number button - */ - protected void setIndicator(View view) { - clearIndicator(); // Does nothing if there was no indicator last selected - TextView tv = (TextView) view; - tv.setTextColor(mSelectedTextColor); - mLastSelectedView = view; - } - - /** - * Clear the selection indicator on the last selected view. Clearing the indicator - * reverts the text color back to its default. - */ - protected void clearIndicator() { - if (mLastSelectedView != null) { - TextView tv = (TextView) mLastSelectedView; - tv.setTextColor(mDefaultTextColor); - mLastSelectedView = null; - } - } - - /** - * @return the index for the number button that should have the indicator set on by default. - * The base implementation returns 0, for the first child. - */ - protected int indexOfDefaultValue() { - return 0; - } - - /** - * @return the number held by the button parsed into an integer. The base implementation - * assumes the view is of type TextView. - */ - protected int valueOf(View button) { - return Integer.parseInt(((TextView) button).getText().toString()); - } - - /** - * The default implementation sets the appropriate text color on all of the number buttons - * as determined by {@link #canRegisterClickListener(View)}. - */ - void setTheme(Context context, boolean themeDark) { - mDefaultTextColor = ContextCompat.getColor(context, themeDark? - R.color.text_color_primary_dark : R.color.text_color_primary_light); - for (int i = 0; i < getChildCount(); i++) { - View v = getChildAt(i); - // TODO: We can move this to the ctor, because this isn't dependent on the theme. - // The only issue is we would have to write another for loop iterating through all - // the buttons... but that is just prematurely worrying about optimizations.. - Utils.setColorControlHighlight(v, mSelectedTextColor/*colorAccent*/); - // Filter out views that aren't number buttons - if (canRegisterClickListener(v)) { - final TextView tv = (TextView) v; - // Filter out the current selection - if (mSelection != valueOf(tv)) { - tv.setTextColor(mDefaultTextColor); - } - } - } - } - - /** - * Iterates through our hierarchy and sets the subclass's implementation of OnClickListener - * on each number button encountered. By default, the number buttons are assumed to be of - * type TextView. - */ - private void registerClickListeners() { - int i = 0; - View v; - while (i < getChildCount() && canRegisterClickListener(v = getChildAt(i))) { - v.setOnClickListener(this); - i++; - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/NumpadTimePicker.java b/app/src/main/java/com/philliphsu/clock2/timepickers/NumpadTimePicker.java deleted file mode 100644 index 04976a3..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/NumpadTimePicker.java +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.ColorStateList; -import android.os.Build; -import android.support.annotation.IntDef; -import android.support.annotation.Nullable; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.content.ContextCompat; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.view.animation.DecelerateInterpolator; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.TextView; - -import com.philliphsu.clock2.R; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.text.DateFormatSymbols; -import java.util.Calendar; - -import butterknife.Bind; -import butterknife.OnClick; -import butterknife.OnLongClick; - -/** - * Created by Phillip Hsu on 7/12/2016. - */ -public class NumpadTimePicker extends GridLayoutNumpad { - // Time can be represented with maximum of 4 digits - private static final int MAX_DIGITS = 4; - - // Formatted time string has a maximum of 8 characters - // in the 12-hour clock, e.g 12:59 AM. Although the 24-hour - // clock should be capped at 5 characters, the difference - // is not significant enough to deal with the separate cases. - private static final int MAX_CHARS = 8; - - // Constant for converting text digits to numeric digits in base-10. - private static final int BASE_10 = 10; - - // AmPmStates - static final int UNSPECIFIED = -1; - static final int AM = 0; - static final int PM = 1; - static final int HRS_24 = 2; - - @IntDef({ UNSPECIFIED, AM, PM, HRS_24 }) // Specifies the accepted constants - @Retention(RetentionPolicy.SOURCE) // Usages do not need to be recorded in .class files - private @interface AmPmState {} - - @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; - @Bind(R.id.backspace) ImageButton mBackspace; - - private boolean mThemeDark; - private int mFabDisabledColorDark; - private int mFabDisabledColorLight; - - @Nullable - private final ObjectAnimator mElevationAnimator; - - /** - * Provides additional APIs to configure clients' display output. - */ - public interface OnInputChangeListener extends GridLayoutNumpad.OnInputChangeListener { - /** - * Called when this numpad's buttons are all disabled, indicating no further - * digits can be inserted. - */ - void onInputDisabled(); - } - - public NumpadTimePicker(Context context) { - this(context, null); - } - - public NumpadTimePicker(Context context, AttributeSet attrs) { - super(context, attrs); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mElevationAnimator = ObjectAnimator.ofFloat(mFab, "elevation", - getResources().getDimension(R.dimen.fab_elevation)) - .setDuration(200); - mElevationAnimator.setInterpolator(new DecelerateInterpolator()); - } else { - // Only animate the elevation for 21+ because changing elevation on pre-21 - // shifts the FAB slightly up/down. For that reason, pre-21 has elevation - // permanently set to 0 (in XML). - mElevationAnimator = null; - } - init(); - } - - @Override - void setTheme(Context context, boolean themeDark) { - super.setTheme(context, themeDark); - mThemeDark = themeDark; - // this.getContext() ==> default teal accent color - // application context ==> white - // The Context that was passed in is NumpadTimePickerDialog.getContext() which - // is probably the host Activity. I have no idea what this.getContext() returns, - // but its probably some internal type that isn't tied to any of our application - // components. - - // So, we kept the 0-9 buttons as TextViews, but here we kept - // the alt buttons as actual Buttons... - for (Button b : mAltButtons) { - setTextColor(b); - Utils.setColorControlHighlight(b, mAccentColor); - } - Utils.setColorControlHighlight(mBackspace, mAccentColor); - - ColorStateList colorBackspace = ContextCompat.getColorStateList(context, - themeDark? R.color.icon_color_dark : R.color.icon_color); - Utils.setTintList(mBackspace, mBackspace.getDrawable(), colorBackspace); - - ColorStateList colorIcon = ContextCompat.getColorStateList(context, - themeDark? R.color.icon_color_dark : R.color.fab_icon_color); - Utils.setTintList(mFab, mFab.getDrawable(), colorIcon); - - // Make sure the dark theme disabled color shows up initially - updateFabState(); - } - - @Override - public int capacity() { - 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; - } - ((OnInputChangeListener) getOnInputChangeListener()).onInputDisabled(); - } - } - - @Override - protected void onDigitInserted(String newDigit) { - // Append the new digit(s) to the formatter - updateFormattedInputOnDigitInserted(newDigit); - super.onDigitInserted(mFormattedInput.toString()); - updateNumpadStates(); - } - - @Override - protected void onDigitDeleted(String newStr) { - updateFormattedInputOnDigitDeleted(); - super.onDigitDeleted(mFormattedInput.toString()); - updateNumpadStates(); - } - - @Override - protected void onDigitsCleared() { - mFormattedInput.delete(0, mFormattedInput.length()); - mAmPmState = UNSPECIFIED; - updateNumpadStates(); // TOneverDO: before resetting mAmPmState to UNSPECIFIED - super.onDigitsCleared(); - } - - @Override - @OnClick(R.id.backspace) - public void delete() { - int len = mFormattedInput.length(); - if (!is24HourFormat() && mAmPmState != UNSPECIFIED) { - mAmPmState = UNSPECIFIED; - // Delete starting from index of space to end - mFormattedInput.delete(mFormattedInput.indexOf(" "), len); - // No digit was actually deleted, but we have to notify the - // listener to update its output. - super/*TOneverDO: remove super*/.onDigitDeleted(mFormattedInput.toString()); - // We also have to manually update the numpad. - updateNumpadStates(); - } else { - super.delete(); - } - } - - @Override - @OnLongClick(R.id.backspace) - public boolean clear() { - return super.clear(); - } - - /** Returns the hour of day (0-23) regardless of clock system */ - public int getHour() { - if (!checkTimeValid()) - throw new IllegalStateException("Cannot call hourOfDay() until legal time inputted"); - int hours = count() < 4 ? valueAt(0) : valueAt(0) * 10 + valueAt(1); - if (hours == 12) { - switch (mAmPmState) { - case AM: - return 0; - case PM: - case HRS_24: - return 12; - default: - break; - } - } - - // AM/PM clock needs value offset - return hours + (mAmPmState == PM ? 12 : 0); - } - - public int getMinute() { - if (!checkTimeValid()) - throw new IllegalStateException("Cannot call minute() until legal time inputted"); - return count() < 4 ? valueAt(1) * 10 + valueAt(2) : valueAt(2) * 10 + valueAt(3); - } - - /** - * Checks if the input stored so far qualifies as a valid time. - * For this to return {@code true}, the hours, minutes AND AM/PM - * state must be set. - */ - public boolean checkTimeValid() { - // While the test looks bare, it is actually comprehensive. - // mAmPmState will remain UNSPECIFIED until a legal - // sequence of digits is inputted, no matter the clock system in use. - // TODO: So if that's the case, do we actually need 'count() < 3' here? Or better yet, - // can we simplify the code to just 'return mAmPmState != UNSPECIFIED'? - if (mAmPmState == UNSPECIFIED || mAmPmState == HRS_24 && count() < 3) { - return false; - } - // AM or PM can only be set if the time was already valid previously, so we don't need - // to check for them. - return true; - } - - public void setTime(int hours, int minutes) { - if (hours < 0 || hours > 23) - throw new IllegalArgumentException("Illegal hours: " + hours); - if (minutes < 0 || minutes > 59) - throw new IllegalArgumentException("Illegal minutes: " + minutes); - - // Internal representation of the time has been checked for legality. - // Now we need to format it depending on the user's clock system. - // If 12-hour clock, can't set mAmPmState yet or else this interferes - // with the button state update mechanism. Instead, cache the state - // the hour would resolve to in a local variable and set it after - // all digits are inputted. - int amPmState; - if (!is24HourFormat()) { - // Convert 24-hour times into 12-hour compatible times. - if (hours == 0) { - hours = 12; - amPmState = AM; - } else if (hours == 12) { - amPmState = PM; - } else if (hours > 12) { - hours -= 12; - amPmState = PM; - } else { - amPmState = AM; - } - } else { - amPmState = HRS_24; - } - - /* - // Convert the hour and minutes into text form, so that - // we can read each digit individually. - // Only if on 24-hour clock, zero-pad single digit hours. - // Zero cannot be the first digit of any time in the 12-hour clock. - String textDigits = is24HourFormat() - ? String.format("%02d", hours) - : String.valueOf(hours); - textDigits += String.format("%02d", minutes); - - int[] digits = new int[textDigits.length()]; - for (int i = 0; i < textDigits.length(); i++) { - digits[i] = Character.digit(textDigits.charAt(i), BASE_10); - } - insertDigits(digits); - */ - - if (is24HourFormat() || hours > 9) { - insertDigits(hours / 10, hours % 10, minutes / 10, minutes % 10); - } else { - insertDigits(hours, minutes / 10, minutes % 10); - } - - mAmPmState = amPmState; - if (mAmPmState != HRS_24) { - onAltButtonClick(mAmPmState == AM ? mAltButtons[0] : mAltButtons[1]); - } - } - - public String getTime() { - return mFormattedInput.toString(); - } - - @AmPmState - int getAmPmState() { - return mAmPmState; - } - - // Because the annotation and its associated enum constants are marked private, the only real - // use for this method is to restore state across rotation after saving the value from - // #getAmPmState(). We can't directly pass in one of those accepted constants. - void setAmPmState(@AmPmState int amPmState) { -// mAmPmState = amPmState; - switch (amPmState) { - case AM: - case PM: - // mAmPmState is set for us - onAltButtonClick(mAltButtons[amPmState]); - break; - case HRS_24: - // Restoring the digits, if they make a valid time, should have already - // restored the mAmPmState to this value for us. If they don't make a - // valid time, then we refrain from setting it. - break; - case UNSPECIFIED: - // We should already be set to this value initially, but it can't hurt? - mAmPmState = amPmState; - break; - } - } - - private void init() { - mFabDisabledColorDark = ContextCompat.getColor(getContext(), R.color.fab_disabled_dark); - mFabDisabledColorLight = ContextCompat.getColor(getContext(), R.color.fab_disabled_light); - // TODO: We should have the user pass in is24HourMode when they create an instance of the dialog. - if (DateFormat.is24HourFormat(getContext())) { - 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]); - } - updateNumpadStates(); - } - - @OnClick({ R.id.leftAlt, R.id.rightAlt }) - void onAltButtonClick(TextView altBtn) { - // 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 - String ampm = altBtn.getText().toString(); - mFormattedInput.append(' ').append(ampm); - String am = new DateFormatSymbols().getAmPmStrings()[0]; - mAmPmState = ampm.equals(am) ? AM : PM; - // Digits will be shown for you on insert, but not AM/PM - super/*TOneverDO: remove 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; - } - - updateNumpadStates(); - } - - private boolean is24HourFormat() { - return DateFormat.is24HourFormat(getContext()); - } - - private void updateFormattedInputOnDigitInserted(String newDigits) { - mFormattedInput.append(newDigits); - // Add colon if necessary, depending on how many digits entered so far - if (count() == 3) { - // Insert a colon - int digits = getInput(); - if (digits >= 60 && digits < 100 || digits >= 160 && digits < 200) { - // From 060-099 (really only to 095, but might as well go up to 100) - // From 160-199 (really only to 195, but might as well go up to 200), - // time does not exist if colon goes at pos. 1 - mFormattedInput.insert(2, ':'); - // These times only apply to the 24-hour clock, and if we're here, - // the time is not legal yet. So we can't set mAmPmState here for - // either clock. - // The 12-hour clock can only have mAmPmState set when AM/PM are clicked. - } else { - // A valid time exists if colon is at pos. 1 - mFormattedInput.insert(1, ':'); - // We can set mAmPmState here (and not in the above case) because - // the time here is legal in 24-hour clock - if (is24HourFormat()) { - mAmPmState = HRS_24; - } - } - } else if (count() == MAX_DIGITS) { - int colonAt = mFormattedInput.indexOf(":"); - // Since we now batch update the formatted input whenever - // digits are inserted, the colon may legitimately not be - // present in the formatted input when this is initialized. - if (colonAt != -1) { - // Colon needs to move, so remove the colon previously added - mFormattedInput.deleteCharAt(colonAt); - } - mFormattedInput.insert(2, ':'); - - // Time is legal in 24-hour clock - if (is24HourFormat()) { - mAmPmState = HRS_24; - } - } - } - - private void updateFormattedInputOnDigitDeleted() { - int len = mFormattedInput.length(); - mFormattedInput.delete(len - 1, len); - if (count() == 3) { - int value = getInput(); - // Move the colon from its 4-digit position to its 3-digit position, - // unless doing so gives an invalid time. - // e.g. 17:55 becomes 1:75, which is invalid. - // All 3-digit times in the 12-hour clock at this point should be - // valid. The limits <=155 and (>=200 && <=235) are really only - // imposed on the 24-hour clock, and were chosen because 4-digit times - // in the 24-hour clock can only go up to 15:5[0-9] or be within the range - // [20:00, 23:59] if they are to remain valid when they become three digits. - // The is24HourFormat() check is therefore unnecessary. - if (value <= 155 || value >= 200 && value <= 235) { - mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":")); - mFormattedInput.insert(1, ":"); - } else { - // previously [16:00, 19:59] - mAmPmState = UNSPECIFIED; - } - } else if (count() == 2) { - // Remove the colon - mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":")); - // No time can be valid with only 2 digits in either system. - // I don't think we actually need this, but it can't hurt? - mAmPmState = UNSPECIFIED; - } - } - - private void updateNumpadStates() { - // TOneverDO: after updateNumberKeysStates(), esp. if clock is 12-hour, - // because it calls enable(0, 0), which checks if the alt buttons have been - // disabled as well before firing the onInputDisabled(). - updateAltButtonStates(); - - updateBackspaceState(); - updateNumberKeysStates(); - updateFabState(); - } - - private void updateFabState() { - final boolean lastEnabled = mFab.isEnabled(); - mFab.setEnabled(checkTimeValid()); - // If the fab was last enabled and we rotate, this check will prevent us from - // restoring the color; it will instead show up opaque white with an eclipse. - // Why isn't the FAB initialized to enabled == false when it is recreated? - // The FAB class probably saves its own state. -// if (lastEnabled == mFab.isEnabled()) -// return; - - // Workaround for mFab.setBackgroundTintList() because I don't know how to reference the - // correct accent color in XML. Also because I don't want to programmatically create a - // ColorStateList. - int color; - if (mFab.isEnabled()) { - color = mAccentColor; - // If FAB was last enabled, then don't run the anim again. - if (mElevationAnimator != null && !lastEnabled) { - mElevationAnimator.start(); - } - } else { - color = mThemeDark? mFabDisabledColorDark : mFabDisabledColorLight; - if (lastEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (mElevationAnimator != null && mElevationAnimator.isRunning()) { - // Otherwise, eclipse will show. - mElevationAnimator.end(); - } - // No animation, otherwise we'll see eclipsing. - mFab.setElevation(0); - } - } - // TODO: How can we animate the background color? There is a ObjectAnimator.ofArgb() - // method, but that uses color ints as values. What we'd really need is something like - // ColorStateLists as values. There is an ObjectAnimator.ofObject(), but I don't know - // how that works. There is also a ValueAnimator.ofInt(), which doesn't need a - // target object. - mFab.setBackgroundTintList(ColorStateList.valueOf(color)); - } - - private void updateBackspaceState() { - mBackspace.setEnabled(count() > 0); - } - - private void updateAltButtonStates() { - if (count() == 0) { - // No input, no access! - mAltButtons[0].setEnabled(false); - mAltButtons[1].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); - } 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); - } 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); - } else { - // True for any 3 digits, if AM/PM not already entered - boolean enabled = mAmPmState == UNSPECIFIED; - mAltButtons[0].setEnabled(enabled); - mAltButtons[1].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); - } - } - - private void updateNumberKeysStates() { - int cap = 10; // number of buttons - boolean is24hours = is24HourFormat(); - - if (count() == 0) { - enable(is24hours ? 0 : 1, cap); - return; - } else if (count() == MAX_DIGITS) { - enable(0, 0); - return; - } - - int time = getInput(); - if (is24hours) { - if (count() == 1) { - enable(0, time < 2 ? cap : 6); - } else if (count() == 2) { - enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 6); - } else if (count() == 3) { - if (time >= 236) { - enable(0, 0); - } else { - enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0); - } - } - } else { - if (count() == 1) { - if (time == 0) { - throw new IllegalStateException("12-hr format, zeroth digit = 0?"); - } else { - enable(0, 6); - } - } else if (count() == 2 || count() == 3) { - if (time >= 126) { - enable(0, 0); - } else { - if (time >= 100 && time <= 125 && mAmPmState != UNSPECIFIED) { - // Could legally input fourth digit, if not for the am/pm state already set - enable(0, 0); - } else { - enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0); - } - } - } - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/NumpadTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/timepickers/NumpadTimePickerDialog.java deleted file mode 100644 index 4c00446..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/NumpadTimePickerDialog.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.philliphsu.clock2.R; -import com.philliphsu.clock2.util.TimeTextUtils; - -import butterknife.Bind; -import butterknife.OnClick; - -/** - * Created by Phillip Hsu on 7/12/2016. - * - */ -// TODO: Use library's NumberPadTimePickerDialog instead. -@Deprecated -public class NumpadTimePickerDialog extends BaseTimePickerDialog - implements NumpadTimePicker.OnInputChangeListener { - private static final String TAG = "NumpadTimePickerDialog"; - - private static final String KEY_IS_24_HOUR_VIEW = "is_24_hour_view"; - private static final String KEY_DIGITS_INPUTTED = "digits_inputted"; - private static final String KEY_AMPM_STATE = "ampm_state"; - private static final String KEY_THEME_DARK = "theme_dark"; - private static final String KEY_THEME_SET_AT_RUNTIME = "theme_set_at_runtime"; - - private boolean mIs24HourMode; // TODO: Why do we need this? - /** - * 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 mAmPmState = NumpadTimePicker.UNSPECIFIED; // TOneverDO: zero initial value, b/c 0 == AM - private boolean mThemeDark; - private boolean mThemeSetAtRuntime; - - // Don't need to keep a reference to the dismiss ImageButton - @Bind(R.id.input_time) TextView mInputField; - @Bind(R.id.number_grid) NumpadTimePicker mNumpad; -// @Bind(R.id.focus_grabber) View mFocusGrabber; - - // TODO: We don't need to pass in an initial hour and minute for a new instance. - @Deprecated - public static NumpadTimePickerDialog newInstance(OnTimeSetListener callback, - int hourOfDay, int minute, boolean is24HourMode) { - NumpadTimePickerDialog ret = new NumpadTimePickerDialog(); - ret.initialize(callback, hourOfDay, minute, is24HourMode); - return ret; - } - - // TODO: is24HourMode param - public static NumpadTimePickerDialog newInstance(OnTimeSetListener callback) { - NumpadTimePickerDialog ret = new NumpadTimePickerDialog(); - // TODO: Do these in initialize() - ret.setOnTimeSetListener(callback); - ret.mThemeDark = false; - ret.mThemeSetAtRuntime = false; - return ret; - } - - @Deprecated - public void initialize(OnTimeSetListener callback, - int hourOfDay, int minute, boolean is24HourMode) { - setOnTimeSetListener(callback); - mIs24HourMode = is24HourMode; - } - - /** - * Set a dark or light theme. NOTE: this will only take effect for the next onCreateView. - */ - public void setThemeDark(boolean dark) { - mThemeDark = dark; - mThemeSetAtRuntime = true; - } - - public boolean isThemeDark() { - return mThemeDark; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - mInputtedDigits = savedInstanceState.getIntArray(KEY_DIGITS_INPUTTED); - mIs24HourMode = savedInstanceState.getBoolean(KEY_IS_24_HOUR_VIEW); - mAmPmState = savedInstanceState.getInt(KEY_AMPM_STATE); - mThemeDark = savedInstanceState.getBoolean(KEY_THEME_DARK); - mThemeSetAtRuntime = savedInstanceState.getBoolean(KEY_THEME_SET_AT_RUNTIME); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = super.onCreateView(inflater, container, savedInstanceState); - if (!mThemeSetAtRuntime) { - mThemeDark = Utils.isDarkTheme(getActivity(), mThemeDark); - } - mNumpad.setOnInputChangeListener(this); - mNumpad.insertDigits(mInputtedDigits); // TOneverDO: before mNumpad.setOnInputChangeListener(this); - mNumpad.setAmPmState(mAmPmState); - // Show the cursor immediately -// mInputField.requestFocus(); - //updateInputText(""); // Primarily to disable 'OK' - - // Prepare colors - int accentColor = Utils.getThemeAccentColor(getContext()); - int lightGray = ContextCompat.getColor(getContext(), R.color.light_gray); - int darkGray = ContextCompat.getColor(getContext(), R.color.dark_gray); - int white = ContextCompat.getColor(getContext(), android.R.color.white); - - // Set background color of entire view - view.setBackgroundColor(mThemeDark? darkGray : white); - - TextView inputTime = (TextView) view.findViewById(R.id.input_time); - inputTime.setBackgroundColor(mThemeDark? lightGray : accentColor); - inputTime.setTextColor(ContextCompat.getColor(getContext(), android.R.color.white)); - - mNumpad.setTheme(getContext()/*DO NOT GIVE THE APPLICATION CONTEXT, OR ELSE THE NUMPAD - CAN'T GET THE CORRECT ACCENT COLOR*/, mThemeDark); - - return view; - } - - @Override - protected int contentLayout() { - return 0; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - if (mNumpad != null) { - outState.putIntArray(KEY_DIGITS_INPUTTED, mNumpad.getDigits()); - outState.putBoolean(KEY_IS_24_HOUR_VIEW, mIs24HourMode); - outState.putInt(KEY_AMPM_STATE, mNumpad.getAmPmState()); - outState.putBoolean(KEY_THEME_DARK, mThemeDark); - outState.putBoolean(KEY_THEME_SET_AT_RUNTIME, mThemeSetAtRuntime); - } - } - - @Override - public void onDigitInserted(String newStr) { - updateInputText(newStr); - } - - @Override - public void onDigitDeleted(String newStr) { - updateInputText(newStr); - } - - @Override - public void onDigitsCleared() { - updateInputText(""); - } - - @Override - public void onInputDisabled() { - // Steals the focus from the EditText -// mFocusGrabber.requestFocus(); - } - -// @OnTouch(R.id.input_time) -// boolean captureTouchOnEditText() { -// // Capture touch events on the EditText field, because we want it to do nothing. -// return true; -// } - - // The FAB is not defined directly in this dialog's layout, but rather in the layout - // of the NumpadTimePicker. We can always reference a child of a ViewGroup that is - // part of our layout. - @OnClick(R.id.fab) - void confirmSelection() { - if (!mNumpad.checkTimeValid()) - return; - onTimeSet(mNumpad, mNumpad.getHour(), mNumpad.getMinute()); - } - - private void updateInputText(String inputText) { - TimeTextUtils.setText(inputText, mInputField); -// // Move the cursor -// mInputField.setSelection(mInputField.length()); -// if (mFocusGrabber.isFocused()) { -// // Return focus to the EditText -// mInputField.requestFocus(); -// } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/TwentyFourHourGridItem.java b/app/src/main/java/com/philliphsu/clock2/timepickers/TwentyFourHourGridItem.java deleted file mode 100644 index 294f879..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/TwentyFourHourGridItem.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.view.Gravity; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.philliphsu.clock2.R; -import com.philliphsu.clock2.util.ConfigurationUtils; - -import butterknife.Bind; -import butterknife.ButterKnife; - -/** - * Created by Phillip Hsu on 7/21/2016. - */ -public class TwentyFourHourGridItem extends LinearLayout { - - @Bind(R.id.primary) TextView mPrimaryText; - @Bind(R.id.secondary) TextView mSecondaryText; - - public TwentyFourHourGridItem(Context context) { - super(context); - init(); - } - - public TwentyFourHourGridItem(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - TypedArray a = context.getTheme().obtainStyledAttributes(attrs, - R.styleable.TwentyFourHourGridItem, 0, 0); - try { - setPrimaryText(a.getString(R.styleable.TwentyFourHourGridItem_primaryText)); - setSecondaryText(a.getString(R.styleable.TwentyFourHourGridItem_secondaryText)); - } finally { - a.recycle(); - } - } - - public CharSequence getPrimaryText() { - return mPrimaryText.getText(); - } - - public void setPrimaryText(CharSequence text) { - mPrimaryText.setText(text); - } - - public CharSequence getSecondaryText() { - return mSecondaryText.getText(); - } - - public void setSecondaryText(CharSequence text) { - mSecondaryText.setText(text); - } - - public void swapTexts() { - CharSequence primary = mPrimaryText.getText(); - setPrimaryText(mSecondaryText.getText()); - setSecondaryText(primary); - } - - public TextView getPrimaryTextView() { - return (TextView) getChildAt(0); - } - - public TextView getSecondaryTextView() { - return (TextView) getChildAt(1); - } - - private void init() { - // TODO: Why isn't ALT-ENTER giving us an option to static import this method? - final int orientation = ConfigurationUtils.getOrientation(getResources()); - setOrientation(orientation == Configuration.ORIENTATION_PORTRAIT ? - VERTICAL : /*LANDSCAPE*/HORIZONTAL); - setGravity(Gravity.CENTER); - inflate(getContext(), R.layout.content_24h_grid_item, this); - ButterKnife.bind(this); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timepickers/TwentyFourHoursGrid.java b/app/src/main/java/com/philliphsu/clock2/timepickers/TwentyFourHoursGrid.java deleted file mode 100644 index d634cd3..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timepickers/TwentyFourHoursGrid.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2016 Phillip Hsu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.philliphsu.clock2.timepickers; - -import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.view.View; - -import com.philliphsu.clock2.R; - -/** - * Created by Phillip Hsu on 8/17/2016. - */ -public class TwentyFourHoursGrid extends NumbersGrid implements View.OnLongClickListener { - private static final String TAG = "TwentyFourHoursGrid"; - - private int mSecondaryTextColor; - - public TwentyFourHoursGrid(Context context) { - super(context); - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).setOnLongClickListener(this); - } - mSecondaryTextColor = ContextCompat.getColor(context, R.color.text_color_secondary_light); - } - - @Override - protected int contentLayout() { - return R.layout.content_24h_number_grid; - } - - @Override - protected boolean canRegisterClickListener(View view) { - return view instanceof TwentyFourHourGridItem; - } - - @Override - public void onClick(View v) { - final int newVal = valueOf(v); - setSelection(newVal); - mSelectionListener.onNumberSelected(newVal); - } - - @Override - public boolean onLongClick(View v) { - TwentyFourHourGridItem item = (TwentyFourHourGridItem) v; - // Unfortunately, we can't use #valueOf() for this because we want the secondary value. - int newVal = Integer.parseInt(item.getSecondaryText().toString()); - mSelectionListener.onNumberSelected(newVal); - // TOneverDO: Call before firing the onNumberSelected() callback, because we want the - // dialog to advance to the next index WITHOUT seeing the text swapping. - swapTexts(); - // TOneverDO: Call before swapping texts, because setIndicator() uses the primary TextView. - setSelection(newVal); - return true; // Consume the long click - } - - @Override - public void setSelection(int value) { - super.setSelection(value); - // The value is within [0, 23], but we have only 12 buttons. - setIndicator(getChildAt(value % 12)); - } - - @Override - protected void setIndicator(View view) { - TwentyFourHourGridItem item = (TwentyFourHourGridItem) view; - super.setIndicator(item.getPrimaryTextView()); - } - - @Override - void setTheme(Context context, boolean themeDark) { - mDefaultTextColor = ContextCompat.getColor(context, themeDark? - R.color.text_color_primary_dark : R.color.text_color_primary_light); - mSecondaryTextColor = ContextCompat.getColor(context, themeDark? - R.color.text_color_secondary_dark : R.color.text_color_secondary_light); - for (int i = 0; i < getChildCount(); i++) { - TwentyFourHourGridItem item = (TwentyFourHourGridItem) getChildAt(i); - // TODO: We could move this to the ctor, in the superclass. If so, then this class - // doesn't need to worry about setting the highlight. - Utils.setColorControlHighlight(item, mSelectedTextColor/*colorAccent*/); - // Filter out the current selection. - if (getSelection() != valueOf(item)) { - item.getPrimaryTextView().setTextColor(mDefaultTextColor); - // The indicator can only be set on the primary text, which is why we don't need - // the secondary text here. - } - item.getSecondaryTextView().setTextColor(mSecondaryTextColor); - } - } - - public void swapTexts() { - for (int i = 0; i < getChildCount(); i++) { - View v = getChildAt(i); - ((TwentyFourHourGridItem) v).swapTexts(); - } - } - - @Override - protected int valueOf(View button) { - return Integer.parseInt(((TwentyFourHourGridItem) button).getPrimaryText().toString()); - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/time_header_label.xml b/app/src/main/res/layout/time_header_label.xml deleted file mode 100644 index 1e83a11..0000000 --- a/app/src/main/res/layout/time_header_label.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/bottomsheetpickers-debug/bottomsheetpickers-debug.aar b/bottomsheetpickers-debug/bottomsheetpickers-debug.aar deleted file mode 100644 index 86b3da4..0000000 Binary files a/bottomsheetpickers-debug/bottomsheetpickers-debug.aar and /dev/null differ diff --git a/bottomsheetpickers-debug/build.gradle b/bottomsheetpickers-debug/build.gradle deleted file mode 100644 index 0d8815b..0000000 --- a/bottomsheetpickers-debug/build.gradle +++ /dev/null @@ -1,2 +0,0 @@ -configurations.maybeCreate("default") -artifacts.add("default", file('bottomsheetpickers-debug.aar')) \ No newline at end of file diff --git a/bottomsheetpickers-release/bottomsheetpickers-release.aar b/bottomsheetpickers-release/bottomsheetpickers-release.aar deleted file mode 100644 index 0296cb0..0000000 Binary files a/bottomsheetpickers-release/bottomsheetpickers-release.aar and /dev/null differ diff --git a/bottomsheetpickers-release/build.gradle b/bottomsheetpickers-release/build.gradle deleted file mode 100644 index 98403e4..0000000 --- a/bottomsheetpickers-release/build.gradle +++ /dev/null @@ -1,2 +0,0 @@ -configurations.maybeCreate("default") -artifacts.add("default", file('bottomsheetpickers-release.aar')) \ No newline at end of file diff --git a/build.gradle b/build.gradle index 754a95d..e9219b9 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 945128d..4a316b5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,22 +1,6 @@ -# -# Copyright (C) 2016 Phillip Hsu -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -#Mon Dec 28 10:00:20 PST 2015 +#Fri Feb 24 23:57:57 PST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/settings.gradle b/settings.gradle index 6652f06..075b744 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,4 +14,4 @@ * limitations under the License. */ -include ':app', ':bottomsheetpickers-release', ':bottomsheetpickers-debug' +include ':app'