From a473f3fd2c958455b13f5f9d8a9806f871179cdf Mon Sep 17 00:00:00 2001 From: Phillip Hsu Date: Sat, 20 Aug 2016 04:05:09 -0700 Subject: [PATCH] Appearance changes to NumberGridTimePickerDialog --- .../com/philliphsu/clock2/MainActivity.java | 7 +- .../clock2/aospdatetimepicker/Utils.java | 64 +-- .../clock2/editalarm/GridSelectorLayout.java | 275 ++++++++++ .../clock2/editalarm/HoursGrid.java | 26 + .../clock2/editalarm/MinutesGrid.java | 71 +++ .../editalarm/NumberGridTimePickerDialog.java | 480 ++++++------------ .../clock2/editalarm/NumbersGrid.java | 143 ++++++ .../clock2/editalarm/NumbersGridView.java | 4 +- .../clock2/editalarm/TwentyFourHoursGrid.java | 69 +++ .../res/layout-v21/content_24h_grid_item.xml | 21 + .../main/res/layout/content_24h_grid_item.xml | 9 +- .../res/layout/content_24h_number_grid.xml | 64 ++- .../main/res/layout/content_hours_grid.xml | 27 + .../main/res/layout/content_minutes_grid.xml | 36 ++ .../main/res/layout/content_number_grid.xml | 15 - .../content_number_grid_minute_tuners.xml | 13 - .../layout/dialog_time_picker_number_grid.xml | 24 +- .../main/res/layout/item_24h_number_grid.xml | 9 - app/src/main/res/layout/item_number_grid.xml | 10 - app/src/main/res/values-v21/dimens.xml | 3 +- .../res/values/aosp_datetimepicker_colors.xml | 10 +- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/dimens.xml | 10 +- app/src/main/res/values/styles.xml | 18 + 24 files changed, 964 insertions(+), 446 deletions(-) create mode 100644 app/src/main/java/com/philliphsu/clock2/editalarm/GridSelectorLayout.java create mode 100644 app/src/main/java/com/philliphsu/clock2/editalarm/HoursGrid.java create mode 100644 app/src/main/java/com/philliphsu/clock2/editalarm/MinutesGrid.java create mode 100644 app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGrid.java create mode 100644 app/src/main/java/com/philliphsu/clock2/editalarm/TwentyFourHoursGrid.java create mode 100644 app/src/main/res/layout-v21/content_24h_grid_item.xml create mode 100644 app/src/main/res/layout/content_hours_grid.xml create mode 100644 app/src/main/res/layout/content_minutes_grid.xml delete mode 100644 app/src/main/res/layout/content_number_grid.xml delete mode 100644 app/src/main/res/layout/content_number_grid_minute_tuners.xml delete mode 100644 app/src/main/res/layout/item_24h_number_grid.xml delete mode 100644 app/src/main/res/layout/item_number_grid.xml diff --git a/app/src/main/java/com/philliphsu/clock2/MainActivity.java b/app/src/main/java/com/philliphsu/clock2/MainActivity.java index f07cad0..abd86cd 100644 --- a/app/src/main/java/com/philliphsu/clock2/MainActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/MainActivity.java @@ -260,8 +260,11 @@ public class MainActivity extends BaseActivity { // has no reference to the AlarmsFragment, but it does have a reference to a // Context (which we can cast to Activity). Thus, ExpandedAlarmViewHolder // uses Activity#startActivityForResult(). - mSectionsPagerAdapter.getFragment(mViewPager.getCurrentItem()) - .onActivityResult(requestCode, resultCode, data); + + // THIS WAS ACTUALLY A BAD IDEA, ESPECIALLY FOR TIMERSFRAGMENT. THIS ENDS UP ADDING + // DUPLICATE TIMERS. +// mSectionsPagerAdapter.getFragment(mViewPager.getCurrentItem()) +// .onActivityResult(requestCode, resultCode, data); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/aospdatetimepicker/Utils.java b/app/src/main/java/com/philliphsu/clock2/aospdatetimepicker/Utils.java index 1938cb3..40b3c2a 100644 --- a/app/src/main/java/com/philliphsu/clock2/aospdatetimepicker/Utils.java +++ b/app/src/main/java/com/philliphsu/clock2/aospdatetimepicker/Utils.java @@ -22,10 +22,8 @@ import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Color; import android.os.Build; import android.support.annotation.AttrRes; -import android.support.v4.content.ContextCompat; import android.text.format.Time; import android.util.TypedValue; import android.view.View; @@ -151,37 +149,45 @@ public class Utils { * @param context The context to use as reference for the color * @return the accent color of the current context */ - // Source from MDTP - public static int getAccentColorFromThemeIfAvailable(Context context) { - TypedValue typedValue = new TypedValue(); - // First, try the android:colorAccent - if (Build.VERSION.SDK_INT >= 21) { - context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); - return typedValue.data; - } - // Next, try colorAccent from support lib - int colorAccentResId = context.getResources().getIdentifier("colorAccent", "attr", context.getPackageName()); - if (colorAccentResId != 0 && context.getTheme().resolveAttribute(colorAccentResId, typedValue, true)) { - return typedValue.data; - } - - return ContextCompat.getColor(context, R.color.accent_color); + public static int getThemeAccentColor(Context context) { + // Source from MDTP +// TypedValue typedValue = new TypedValue(); +// // First, try the android:colorAccent +// if (Build.VERSION.SDK_INT >= 21) { +// context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); +// return typedValue.data; +// } +// // Next, try colorAccent from support lib +// int colorAccentResId = context.getResources().getIdentifier("colorAccent", "attr", context.getPackageName()); +// if (colorAccentResId != 0 && context.getTheme().resolveAttribute(colorAccentResId, typedValue, true)) { +// return typedValue.data; +// } +// +// return ContextCompat.getColor(context, R.color.accent_color); + return getColorFromThemeAttr(context, R.attr.colorAccent); } - // See http://stackoverflow.com/a/4928826/5055032 - public static int darkenColor(int color) { - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] *= 0.8f; // value component - return Color.HSVToColor(hsv); + public static int getTextColorPrimary(Context context) { + // http://stackoverflow.com/a/33839580/5055032 + final TypedValue value = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, value, true); + TypedArray a = context.obtainStyledAttributes(value.data, + new int[] {android.R.attr.textColorPrimary}); + final int color = a.getColor(0/*index*/, 0/*defValue*/); + a.recycle(); + return color; + // Didn't work! Gave me white! +// return getColorFromThemeAttr(context, android.R.attr.textColorPrimary); } - // See http://stackoverflow.com/a/4928826/5055032 - public static int lightenColor(int color) { - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] = 1.0f - 0.8f * (1.0f - hsv[2]); - return Color.HSVToColor(hsv); + /** + * @param resId The resource identifier of the desired theme attribute. + */ + public static int getColorFromThemeAttr(Context context, int resId) { + // http://stackoverflow.com/a/28777489/5055032 + final TypedValue value = new TypedValue(); + context.getTheme().resolveAttribute(resId, value, true); + return value.data; } /** diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/GridSelectorLayout.java b/app/src/main/java/com/philliphsu/clock2/editalarm/GridSelectorLayout.java new file mode 100644 index 0000000..3d464d7 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/GridSelectorLayout.java @@ -0,0 +1,275 @@ +/* + * 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.editalarm; + +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.FrameLayout; + +/** + * Created by Phillip Hsu on 8/17/2016. + * + * A derivative of the AOSP datetimepicker RadialPickerLayout class. + */ +public class GridSelectorLayout extends FrameLayout 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 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(); + + public interface OnValueSelectedListener { + void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance); + } + + public GridSelectorLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + // 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; + } + + mIs24HourMode = is24HourMode; + if (is24HourMode) { + m24HoursGrid = new TwentyFourHoursGrid(context); + m24HoursGrid.initialize(this/*OnNumberSelectedListener*/); + addView(m24HoursGrid); + } else { + mHoursGrid = new HoursGrid(context); + mHoursGrid.initialize(this/*OnNumberSelectedListener*/); + addView(mHoursGrid); + } + mMinutesGrid = new MinutesGrid(context); + mMinutesGrid.initialize(this/*OnNumberSelectedListener*/); + + // Initialize the currently-selected hour and minute. + setValueForItem(HOUR_INDEX, initialHoursOfDay); + setValueForItem(MINUTE_INDEX, initialMinutes); + + // Record the selected values in the number grids. + if (!is24HourMode) { + initialHoursOfDay = initialHoursOfDay % 12; + if (initialHoursOfDay == 0) { + initialHoursOfDay = 12; + } + mHoursGrid.setSelection(initialHoursOfDay); + } else { + m24HoursGrid.setSelection(initialHoursOfDay); + } + mMinutesGrid.setSelection(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); + + // TODO: These aren't doing much currently, if at all. + 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) { + setItem(HOUR_INDEX, hours); + setItem(MINUTE_INDEX, minutes); + } + + /** + * Set either the hour or the minute. Will set the internal value, and set the selection. + */ + private void setItem(int index, int value) { + if (index == HOUR_INDEX) { + setValueForItem(HOUR_INDEX, value); + if (mIs24HourMode) { + m24HoursGrid.setSelection(value); + } else { + mHoursGrid.setSelection(value); + } + } else if (index == MINUTE_INDEX) { + setValueForItem(MINUTE_INDEX, value); + mMinutesGrid.setSelection(value); + } + } + + 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) { + removeViewAt(0); // We could also call removeAllViews(), since we only have one child. + // We already verified that the index is either HOUR_INDEX or MINUTE_INDEX + addView(index == HOUR_INDEX ? + (mIs24HourMode ? m24HoursGrid : mHoursGrid) + : mMinutesGrid); + } + } + + // 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) { + if (getCurrentItemShowing() == HOUR_INDEX && !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; + } + } + +// final int value = number; +// mHandler.postDelayed(new Runnable() { +// @Override +// public void run() { +// mListener.onValueSelected(HOUR_INDEX, value, true); +// } +// }, ADVANCE_PAGE_DELAY); +// mListener.onValueSelected(HOUR_INDEX, value, true); +// } else { +// mListener.onValueSelected(getCurrentItemShowing(), number, false); +// } + setValueForItem(getCurrentItemShowing(), number); + mListener.onValueSelected(getCurrentItemShowing(), 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) { + if (index == HOUR_INDEX) { + mCurrentHoursOfDay = value; + } else if (index == MINUTE_INDEX){ + mCurrentMinutes = value; + } else if (index == AMPM_INDEX) { + if (value == HALF_DAY_1) { + mCurrentHoursOfDay = mCurrentHoursOfDay % 12; + } else if (value == HALF_DAY_2) { + mCurrentHoursOfDay = (mCurrentHoursOfDay % 12) + 12; + } + } + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/HoursGrid.java b/app/src/main/java/com/philliphsu/clock2/editalarm/HoursGrid.java new file mode 100644 index 0000000..372b916 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/HoursGrid.java @@ -0,0 +1,26 @@ +package com.philliphsu.clock2.editalarm; + +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 + 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/editalarm/MinutesGrid.java b/app/src/main/java/com/philliphsu/clock2/editalarm/MinutesGrid.java new file mode 100644 index 0000000..17dc954 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/MinutesGrid.java @@ -0,0 +1,71 @@ +package com.philliphsu.clock2.editalarm; + +import android.content.Context; +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; + setIndicator(value); + setSelection(value); + mSelectionListener.onNumberSelected(value); + } + }); + mPlusButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + int value = getSelection() + 1; + if (value == 60) + value = 0; + setIndicator(value); + setSelection(value); + mSelectionListener.onNumberSelected(value); + } + }); + } + + @Override + protected int contentLayout() { + return R.layout.content_minutes_grid; + } + + @Override + void setTheme(Context context, boolean themeDark) { + super.setTheme(context, themeDark); + mMinusButton.setImageResource(themeDark? R.drawable.ic_minus_circle_dark_24dp : R.drawable.ic_minus_circle_24dp); + mPlusButton.setImageResource(themeDark? R.drawable.ic_add_circle_dark_24dp : R.drawable.ic_add_circle_24dp); + } + + /** + * Helper method for minute tuners to set the indicator. + * @param value the new value set by the minute tuners + */ + private void setIndicator(int value) { + clearIndicator(); + if (value % 5 == 0) { + // The new value is one of the predetermined minute values + int positionOfValue = value / 5; + setIndicator(getChildAt(positionOfValue)); + } + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumberGridTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumberGridTimePickerDialog.java index ddf3f58..ba13460 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumberGridTimePickerDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumberGridTimePickerDialog.java @@ -18,11 +18,10 @@ package com.philliphsu.clock2.editalarm; import android.animation.ObjectAnimator; import android.app.ActionBar.LayoutParams; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.os.Bundle; -import android.os.Handler; import android.support.design.widget.FloatingActionButton; -import android.support.v7.widget.GridLayout; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -36,6 +35,7 @@ import android.widget.TextView; import com.philliphsu.clock2.R; import com.philliphsu.clock2.aospdatetimepicker.Utils; +import com.philliphsu.clock2.editalarm.GridSelectorLayout.OnValueSelectedListener; import java.text.DateFormatSymbols; import java.util.ArrayList; @@ -53,8 +53,12 @@ import static com.philliphsu.clock2.util.ConversionUtils.dpToPx; /** * Dialog to set a time. + * + * A derivative of the AOSP datetimepicker TimePickerDialog class. + * + * TODO: Rename to GridSelectorTimePickerDialog */ -public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFragment implements OnValueSelectedListener*/{ +public class NumberGridTimePickerDialog extends BaseTimePickerDialog implements OnValueSelectedListener { private static final String TAG = "TimePickerDialog"; private static final String KEY_HOUR_OF_DAY = "hour_of_day"; @@ -68,15 +72,16 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra 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: (Me) Use HALF_DAY_1 instead + * TODO: Use HALF_DAY_1 instead */ public static final int AM = 0; /** - * TODO: (Me) Use HALF_DAY_2 instead + * TODO: Use HALF_DAY_2 instead */ public static final int PM = 1; @@ -93,7 +98,7 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra private TextView mMinuteSpaceView; private TextView mAmPmTextView; private View mAmPmHitspace; -// private RadialPickerLayout mTimePicker; + private GridSelectorLayout mTimePicker; private int mSelectedColor; private int mUnselectedColor; @@ -123,27 +128,18 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra private String mSelectMinutes; // ====================================== MY STUFF ============================================= - private static final int[] HOURS_12 = {1,2,3,4,5,6,7,8,9,10,11,12}; - private static final int[] HOURS_24_HALF_DAY_1 = {0,1,2,3,4,5,6,7,8,9,10,11}; - private static final int[] HOURS_24_HALF_DAY_2 = {12,13,14,15,16,17,18,19,20,21,22,23}; - private static final int[] MINUTES = {0,5,10,15,20,25,30,35,40,45,50,55}; - // The delay in ms before a OnLongClick on a TwentyFourHourGridItem defers to OnClick - private static final int LONG_CLICK_RESULT_LEEWAY = 150; // The padding in dp for the half day icon compound drawable public static final int HALF_DAY_ICON_PADDING = 8; - // TODO: Private? // 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; - private int mCurrentIndex = HOUR_INDEX; - private int mSelectedHalfDay = HALF_DAY_1; - private int mSelectedHourOfDay; - private int mSelectedMinute; - private Handler mHandler; - - @Bind(R.id.grid_layout) GridLayout mGridLayout; + // 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 @@ -156,213 +152,70 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra return R.layout.dialog_time_picker_number_grid; } - private void setNumberTexts() { - if (mCurrentIndex != HOUR_INDEX && mCurrentIndex != MINUTE_INDEX) { - Log.e(TAG, "TimePicker does not support view at index "+mCurrentIndex); - return; - } - - // Set the appropriate texts on each view - for (int i = 0; i < mGridLayout.getChildCount(); i++) { - View v = mGridLayout.getChildAt(i); - if (mCurrentIndex == MINUTE_INDEX || mCurrentIndex == HOUR_INDEX && !mIs24HourMode) { - if (!(v instanceof TextView)) - return; // Reached the ImageButtons - TextView tv = (TextView) v; - tv.setText(mCurrentIndex == MINUTE_INDEX - ? String.format("%02d", MINUTES[i]) - : String.valueOf(HOURS_12[i])); - } else if (mCurrentIndex == HOUR_INDEX && mIs24HourMode) { - TwentyFourHourGridItem item = (TwentyFourHourGridItem) v; - String s1 = String.format("%02d", HOURS_24_HALF_DAY_1[i]); - String s2 = String.valueOf(HOURS_24_HALF_DAY_2[i]); - if (mSelectedHalfDay == HALF_DAY_1) { - item.setPrimaryText(s1); - item.setSecondaryText(s2); - } else if (mSelectedHalfDay == HALF_DAY_2) { - item.setPrimaryText(s2); - item.setSecondaryText(s1); - } else { - Log.e(TAG, "mSelectedHalfDay = " + mSelectedHalfDay + "?"); - } - } - } - } - - // TODO: boolean animate param??? - private void setCurrentItemShowing(int index) { - if (index != HOUR_INDEX && index != MINUTE_INDEX) { - Log.e(TAG, "TimePicker does not support view at index "+index); - return; - } - - int lastIndex = mCurrentIndex; - mCurrentIndex = index; - - if (index != lastIndex) { - if (mIs24HourMode) { - // Hours layout and normal layout use different Views for their grid items, - // so we have to start fresh. - mGridLayout.removeAllViews(); - int layout = index == HOUR_INDEX ? R.layout.content_24h_number_grid : R.layout.content_number_grid; - View.inflate(getActivity(), layout, mGridLayout); - - // TOneverDO: call after inflating minute tuner buttons - setNumberTexts(); - setClickListenersOnButtons(); - //end TOneverDO - } else { - if (index == HOUR_INDEX) { - // Remove the minute tuners - mGridLayout.removeViews(mGridLayout.getChildCount() - 2, 2); - } - // We can reuse the existing child Views, just change the texts. - // They already have the click listener set. - setNumberTexts(); - } - - if (index == MINUTE_INDEX) { - createMinuteTuners(); - } - } - } - - private void setClickListenersOnButtons() { - for (int i = 0; i < mGridLayout.getChildCount(); i++) { - // TODO: Consider leaving out the minute tuner buttons - View v = mGridLayout.getChildAt(i); - v.setOnClickListener(mOnNumberClickListener); - if (v instanceof TwentyFourHourGridItem) { - v.setOnLongClickListener(mOn24HourItemLongClickListener); - } - } - } - @OnClick({ R.id.half_day_toggle_1, R.id.half_day_toggle_2 }) void onHalfDayToggleClick(View v) { - int halfDay = v == mLeftHalfDayToggle ? HALF_DAY_1 : HALF_DAY_2; - if (halfDay != mSelectedHalfDay) { - toggleHalfDay(); + 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; +// } + updateAmPmDisplay(halfDay); + mTimePicker.setAmOrPm(halfDay); } } - private void toggleHalfDay() { +// private void toggleHalfDay() { // int amOrPm = mTimePicker.getIsCurrentlyAmOrPm(); - int amOrPm = mSelectedHalfDay; - // TODO: Use HALF_DAY_1 and 2 instead - if (amOrPm == AM) { - amOrPm = PM; - } else if (amOrPm == PM){ - amOrPm = AM; - } - updateAmPmDisplay(amOrPm); +// // TODO: Use HALF_DAY_1 and 2 instead +// if (amOrPm == AM) { +// amOrPm = PM; +// } else if (amOrPm == PM){ +// amOrPm = AM; +// } +// updateAmPmDisplay(amOrPm); // mTimePicker.setAmOrPm(amOrPm); - mSelectedHalfDay = 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); +// } - if (mIs24HourMode) { - if (mCurrentIndex == HOUR_INDEX) { - for (int i = 0; i < mGridLayout.getChildCount(); i++) { - View v = mGridLayout.getChildAt(i); - ((TwentyFourHourGridItem) v).swapTexts(); - } - } - } - - // TODO: Verify the corresponding TwentyFourHourGridItem retains its indicator - if (amOrPm == HALF_DAY_1) { - mSelectedHourOfDay %= 12; - } else if (amOrPm == HALF_DAY_2) { - mSelectedHourOfDay = (mSelectedHourOfDay % 12) + 12; - } - onValueSelected(HOUR_INDEX, mSelectedHourOfDay, false); - } - - private void createMinuteTuners() { - // https://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html - // "When inflating a layout starting with a , you *must* specify a parent ViewGroup - // and you must set attachToRoot to true (see the documentation of the LayoutInflater#inflate() method)" - // Note that by passing in a non-null parent, this will pass in true for attachToRoot. - View.inflate(getActivity(), R.layout.content_number_grid_minute_tuners, mGridLayout); - int childCount = mGridLayout.getChildCount(); - // The tuner buttons are always the last two children in the grid - mGridLayout.getChildAt(childCount - 2).setOnClickListener(mOnDecrementMinuteListener); - mGridLayout.getChildAt(childCount - 1).setOnClickListener(mOnIncrementMinuteListener); - } - - // TODO: Break this into two OnClickListeners instead--one for normal TextViews and - // the other for TwentyFourHourGridItem. - // TODO: Set the indicator - 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); - } - }; - - private final View.OnLongClickListener mOn24HourItemLongClickListener = new View.OnLongClickListener() { - @Override - public boolean onLongClick(final View v) { - // TODO: Do we need this if we already check this before setting the listener on the view? - if (!(v instanceof TwentyFourHourGridItem)) - return false; - toggleHalfDay(); - mOnNumberClickListener.onClick(v); - return true; - } - }; - - private final OnClickListener mOnIncrementMinuteListener = new OnClickListener() { - @Override - public void onClick(View v) { - // Don't need to check for minute overflow, because setMinute() - // sets minute to 0 if we pass in 60 - onValueSelected(MINUTE_INDEX, mSelectedMinute + 1, false); - } - }; - - private final OnClickListener mOnDecrementMinuteListener = new OnClickListener() { - @Override - public void onClick(View v) { - int value = mSelectedMinute - 1; - if (value < 0) - value = 59; - onValueSelected(MINUTE_INDEX, value, 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); +// } +// }; // ============================================================================================= -// /** -// * The callback interface used to indicate the user is done filling in -// * the time (they clicked on the 'Set' button). -// */ -// public interface OnTimeSetListener { -// -// /** -// * @param view The view associated with this listener. -// * @param hourOfDay The hour that was set. -// * @param minute The minute that was set. -// */ -// void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute); -// } - public NumberGridTimePickerDialog() { // Empty constructor required for dialog fragment. } @@ -379,23 +232,6 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra return ret; } - /** - * @param timeFieldIndex The index representing the time field whose values, ranging from its natural - * lower and upper limits, will be presented as choices in the GridLayout - * contained in this dialog's layout. Must be one of {@link #HOUR_INDEX} - * or {@link #MINUTE_INDEX}. TODO: Why do we need this? - * @param initialHalfDay The half-day, a.k.a. AM/PM for 12-hour time, that this picker should be - * initialized to. Must be one of {@link #HALF_DAY_1} or {@link #HALF_DAY_2}. - * TODO: Why do we need this? - */ - @Deprecated - public static NumberGridTimePickerDialog newInstance(int timeFieldIndex, int initialHalfDay) { - NumberGridTimePickerDialog dialog = new NumberGridTimePickerDialog(); - dialog.mCurrentIndex = timeFieldIndex; - dialog.mSelectedHalfDay = initialHalfDay; - return dialog; - } - public void initialize(OnTimeSetListener callback, int hourOfDay, int minute, boolean is24HourMode) { mCallback = callback; // TODO: Use setOnTimeSetListener() instead? @@ -405,8 +241,6 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra mIs24HourMode = is24HourMode; mInKbMode = false; mThemeDark = false; - - mSelectedHalfDay = hourOfDay < 12 ? HALF_DAY_1 : HALF_DAY_2; } /** @@ -420,6 +254,7 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra return mThemeDark; } + // Defined as final in our base class. // public void setOnTimeSetListener(OnTimeSetListener callback) { // mCallback = callback; // } @@ -433,16 +268,13 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // The Activity is created at this point -// mIs24HourMode = DateFormat.is24HourFormat(getActivity()); - mHandler = new Handler(); 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); + mInKbMode = savedInstanceState.getBoolean(KEY_IN_KB_MODE); mThemeDark = savedInstanceState.getBoolean(KEY_DARK_THEME); } } @@ -458,23 +290,16 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra View view = super.onCreateView(inflater, container, savedInstanceState); - // Inflate the buttons into the grid - int layout = mIs24HourMode ? R.layout.content_24h_number_grid : R.layout.content_number_grid; - View.inflate(getActivity(), layout, mGridLayout); - setNumberTexts(); - setClickListenersOnButtons(); - if (mCurrentIndex == MINUTE_INDEX) { - createMinuteTuners(); - } - 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(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); @@ -506,12 +331,11 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra } // mHapticFeedbackController = new HapticFeedbackController(getActivity()); - -// mTimePicker = (RadialPickerLayout) view.findViewById(R.id.time_picker); -// mTimePicker.setOnValueSelectedListener(this); + mTimePicker = (GridSelectorLayout) view.findViewById(R.id.time_picker); + mTimePicker.setOnValueSelectedListener(this); // mTimePicker.setOnKeyListener(keyboardListener); -// mTimePicker.initialize(getActivity(), mHapticFeedbackController, mInitialHourOfDay, -// mInitialMinute, mIs24HourMode); + mTimePicker.initialize(getActivity(), /*mHapticFeedbackController,*/ mInitialHourOfDay, + mInitialMinute, mIs24HourMode); int currentItemShowing = HOUR_INDEX; if (savedInstanceState != null && @@ -519,7 +343,7 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra currentItemShowing = savedInstanceState.getInt(KEY_CURRENT_ITEM_SHOWING); } setCurrentItemShowing(currentItemShowing, false, true, true); -// mTimePicker.invalidate(); + mTimePicker.invalidate(); mHourView.setOnClickListener(new OnClickListener() { @Override @@ -537,6 +361,7 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra }); // mDoneButton = (TextView) view.findViewById(R.id.done_button); + // This is our FAB. mDoneButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -545,12 +370,8 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra } else { tryVibrate(); } - Log.i(TAG, String.format("Selected time is %02d:%02d", mSelectedHourOfDay, mSelectedMinute)); if (mCallback != null) { -// mCallback.onTimeSet(mTimePicker, mTimePicker.getHours(), mTimePicker.getMinutes()); - // I don't think the listener actually uses the first param passed back, - // so passing null is fine. - mCallback.onTimeSet(null, mSelectedHourOfDay, mSelectedMinute); + mCallback.onTimeSet(mTimePicker, mTimePicker.getHours(), mTimePicker.getMinutes()); } dismiss(); } @@ -574,15 +395,14 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra @Override public void onClick(View v) { tryVibrate(); -// int amOrPm = mTimePicker.getIsCurrentlyAmOrPm(); -// if (amOrPm == AM) { -// amOrPm = PM; -// } else if (amOrPm == PM){ -// amOrPm = AM; -// } -// updateAmPmDisplay(amOrPm); -// mTimePicker.setAmOrPm(amOrPm); - toggleHalfDay(); + int amOrPm = mTimePicker.getIsCurrentlyAmOrPm(); + if (amOrPm == AM) { + amOrPm = PM; + } else if (amOrPm == PM){ + amOrPm = AM; + } + updateAmPmDisplay(amOrPm); + mTimePicker.setAmOrPm(amOrPm); } }); } @@ -606,30 +426,44 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra } // Set the theme at the end so that the initialize()s above don't counteract the theme. -// mTimePicker.setTheme(getActivity().getApplicationContext(), mThemeDark); + 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 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); + 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.findViewById(R.id.time_display_background).setBackgroundColor(mThemeDark? darkGray : white); - view.findViewById(R.id.time_display).setBackgroundColor(mThemeDark? darkGray : white); - ((TextView) view.findViewById(R.id.separator)).setTextColor(mThemeDark? white : timeDisplay); - ((TextView) view.findViewById(R.id.ampm_label)).setTextColor(mThemeDark? white : timeDisplay); + 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); // mDoneButton.setTextColor(mThemeDark? darkDoneTextColor : doneTextColor); -// mTimePicker.setBackgroundColor(mThemeDark? lightGray : circleBackground); + // The AOSP timepicker originally uses these colors for the CircleView + mTimePicker.setBackgroundColor(mThemeDark? /*lightGray : circleBackground*/ darkGray : white); // mDoneButton.setBackgroundResource(mThemeDark? darkDoneBackground : doneBackground); + // http://stackoverflow.com/a/32031019/5055032 + // 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*/); + view.findViewById(R.id.half_day_toggles).setBackgroundColor(mThemeDark? /*lightGray : circleBackground*/ darkGray : white); + return view; } @@ -652,11 +486,11 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra private void updateAmPmDisplay(int amOrPm) { if (amOrPm == AM) { mAmPmTextView.setText(mAmText); -// Utils.tryAccessibilityAnnounce(mTimePicker, mAmText); + Utils.tryAccessibilityAnnounce(mTimePicker, mAmText); mAmPmHitspace.setContentDescription(mAmText); } else if (amOrPm == PM){ mAmPmTextView.setText(mPmText); -// Utils.tryAccessibilityAnnounce(mTimePicker, mPmText); + Utils.tryAccessibilityAnnounce(mTimePicker, mPmText); mAmPmHitspace.setContentDescription(mPmText); } else { mAmPmTextView.setText(mDoublePlaceholderText); @@ -665,28 +499,23 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra @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.putInt(KEY_HOUR_OF_DAY, mSelectedHourOfDay); - outState.putInt(KEY_MINUTE, mSelectedMinute); - outState.putBoolean(KEY_IS_24_HOUR_VIEW, mIs24HourMode); - outState.putInt(KEY_CURRENT_ITEM_SHOWING, mCurrentIndex); - outState.putBoolean(KEY_DARK_THEME, mThemeDark); + 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); + } } -// /** -// * Called by the picker for updating the header display. -// */ -// @Override + /** + * 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); @@ -695,13 +524,13 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra setCurrentItemShowing(MINUTE_INDEX, true, true, false); announcement += ". " + mSelectMinutes; } else { -// mTimePicker.setContentDescription(mHourPickerDescription + ": " + newValue); + mTimePicker.setContentDescription(mHourPickerDescription + ": " + newValue); } -// Utils.tryAccessibilityAnnounce(mTimePicker, announcement); + Utils.tryAccessibilityAnnounce(mTimePicker, announcement); } else if (pickerIndex == MINUTE_INDEX){ setMinute(newValue); -// mTimePicker.setContentDescription(mMinutePickerDescription + ": " + newValue); + mTimePicker.setContentDescription(mMinutePickerDescription + ": " + newValue); } else if (pickerIndex == AMPM_INDEX) { updateAmPmDisplay(newValue); } else if (pickerIndex == ENABLE_PICKER_INDEX) { @@ -713,9 +542,6 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra } private void setHour(int value, boolean announce) { - // TOneverDO: Set after if-else block (modulo operation changes value!) - mSelectedHourOfDay = value; - String format; if (mIs24HourMode) { format = "%02d"; @@ -731,7 +557,7 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra mHourView.setText(text); mHourSpaceView.setText(text); if (announce) { -// Utils.tryAccessibilityAnnounce(mTimePicker, text); + Utils.tryAccessibilityAnnounce(mTimePicker, text); } } @@ -740,37 +566,33 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog /*DialogFra value = 0; } CharSequence text = String.format(Locale.getDefault(), "%02d", value); -// Utils.tryAccessibilityAnnounce(mTimePicker, text); + Utils.tryAccessibilityAnnounce(mTimePicker, text); mMinuteView.setText(text); mMinuteSpaceView.setText(text); - - // Setting this here is fine. - mSelectedMinute = value; } // Show either Hours or Minutes. private void setCurrentItemShowing(int index, boolean animateCircle, boolean delayLabelAnimate, boolean announce) { -// mTimePicker.setCurrentItemShowing(index, animateCircle); - setCurrentItemShowing(index); + 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); -// } + 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); -// } + int minutes = mTimePicker.getMinutes(); + mTimePicker.setContentDescription(mMinutePickerDescription + ": " + minutes); + if (announce) { + Utils.tryAccessibilityAnnounce(mTimePicker, mSelectMinutes); + } labelToAnimate = mMinuteView; } diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGrid.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGrid.java new file mode 100644 index 0000000..05faeaa --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGrid.java @@ -0,0 +1,143 @@ +package com.philliphsu.clock2.editalarm; + +import android.content.Context; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.GridLayout; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import com.philliphsu.clock2.aospdatetimepicker.Utils; + +/** + * 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; + + private final int mDefaultTextColor; + private final int mSelectedTextColor; + + 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 = Utils.getTextColorPrimary(context); + mSelectedTextColor = Utils.getThemeAccentColor(context); + // Show the first button as default selected + setIndicator(getChildAt(indexOfDefaultValue())); + } + + 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) { + TextView tv = (TextView) v; + clearIndicator(); // Does nothing if there was no indicator last selected + setIndicator(v); + mSelection = Integer.parseInt(tv.getText().toString()); + 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) { + 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; + } + + void setTheme(Context context, boolean themeDark) { + // TODO: Change background color and text color + } + + /** + * 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/editalarm/NumbersGridView.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGridView.java index b462444..2331364 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGridView.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGridView.java @@ -111,7 +111,7 @@ public class NumbersGridView extends GridLayout { @Override public void onClick(View v) { setNumbers(new int[] {0,5,10,15,20,25,30,35,40,45,50,55}, true); - inflate(getContext(), R.layout.content_number_grid_minute_tuners, NumbersGridView.this); + inflate(getContext(), R.layout.content_minutes_grid, NumbersGridView.this); } }); i++; @@ -145,7 +145,7 @@ public class NumbersGridView extends GridLayout { boolean is24HourMode = DateFormat.is24HourFormat(getContext()); int layout = is24HourMode ? R.layout.content_24h_number_grid - : R.layout.content_number_grid; + : R.layout.content_hours_grid; inflate(getContext(), layout, this); if (!is24HourMode) { setNumbers(new int[] {1,2,3,4,5,6,7,8,9,10,11,12}); diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/TwentyFourHoursGrid.java b/app/src/main/java/com/philliphsu/clock2/editalarm/TwentyFourHoursGrid.java new file mode 100644 index 0000000..768df13 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/TwentyFourHoursGrid.java @@ -0,0 +1,69 @@ +package com.philliphsu.clock2.editalarm; + +import android.content.Context; +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"; + + public TwentyFourHoursGrid(Context context) { + super(context); + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).setOnLongClickListener(this); + } + } + + @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) { + // We already verified that this view can have a click listener registered on. + // See canRegisterClickListener(). + TwentyFourHourGridItem item = (TwentyFourHourGridItem) v; + clearIndicator(); + setIndicator(v); + int newVal = Integer.parseInt(item.getPrimaryText().toString()); + setSelection(newVal); + mSelectionListener.onNumberSelected(newVal); + } + + @Override + public boolean onLongClick(View v) { + TwentyFourHourGridItem item = (TwentyFourHourGridItem) v; + int newVal = Integer.parseInt(item.getSecondaryText().toString()); + setSelection(newVal); + mSelectionListener.onNumberSelected(newVal); + clearIndicator(); + swapTexts(); + // TOneverDO: Call before swapping texts, because setIndicator() uses the primary TextView. + setIndicator(v); + return true; // Consume the long click + } + + @Override + protected void setIndicator(View view) { + TwentyFourHourGridItem item = (TwentyFourHourGridItem) view; + // Set indicator on the primary TextView + super.setIndicator(item.getChildAt(0)); + } + + public void swapTexts() { + for (int i = 0; i < getChildCount(); i++) { + View v = getChildAt(i); + ((TwentyFourHourGridItem) v).swapTexts(); + } + } +} diff --git a/app/src/main/res/layout-v21/content_24h_grid_item.xml b/app/src/main/res/layout-v21/content_24h_grid_item.xml new file mode 100644 index 0000000..4f5e728 --- /dev/null +++ b/app/src/main/res/layout-v21/content_24h_grid_item.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_24h_grid_item.xml b/app/src/main/res/layout/content_24h_grid_item.xml index 8a4c54e..ca7df05 100644 --- a/app/src/main/res/layout/content_24h_grid_item.xml +++ b/app/src/main/res/layout/content_24h_grid_item.xml @@ -7,12 +7,15 @@ android:id="@+id/primary" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/colorPrimary" - android:textSize="@dimen/grid_element_text_size"/> + android:fontFamily="sans-serif-light" + android:textSize="@dimen/number_grid_24_hour_item_primary_text_size" + android:includeFontPadding="false" + style="@style/TextAppearance.AppCompat"/> + android:layout_height="wrap_content" + android:includeFontPadding="false"/> \ No newline at end of file diff --git a/app/src/main/res/layout/content_24h_number_grid.xml b/app/src/main/res/layout/content_24h_number_grid.xml index cf19b77..3800f4f 100644 --- a/app/src/main/res/layout/content_24h_number_grid.xml +++ b/app/src/main/res/layout/content_24h_number_grid.xml @@ -1,15 +1,53 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_hours_grid.xml b/app/src/main/res/layout/content_hours_grid.xml new file mode 100644 index 0000000..4cb250e --- /dev/null +++ b/app/src/main/res/layout/content_hours_grid.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_minutes_grid.xml b/app/src/main/res/layout/content_minutes_grid.xml new file mode 100644 index 0000000..82c678f --- /dev/null +++ b/app/src/main/res/layout/content_minutes_grid.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_number_grid.xml b/app/src/main/res/layout/content_number_grid.xml deleted file mode 100644 index 56d39df..0000000 --- a/app/src/main/res/layout/content_number_grid.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/content_number_grid_minute_tuners.xml b/app/src/main/res/layout/content_number_grid_minute_tuners.xml deleted file mode 100644 index 8b5eb44..0000000 --- a/app/src/main/res/layout/content_number_grid_minute_tuners.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_time_picker_number_grid.xml b/app/src/main/res/layout/dialog_time_picker_number_grid.xml index 737e660..cf15a8a 100644 --- a/app/src/main/res/layout/dialog_time_picker_number_grid.xml +++ b/app/src/main/res/layout/dialog_time_picker_number_grid.xml @@ -23,24 +23,24 @@ android:layout_gravity="center" /> - + android:layout_below="@id/time_picker"/> @@ -56,8 +56,7 @@ android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="center" - style="@style/TextAppearance.AppCompat.Button" - android:textSize="@dimen/half_day_text_size"/> + style="@style/TextAppearance.AppCompat.Button"/> @@ -73,8 +72,7 @@ android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="center" - style="@style/TextAppearance.AppCompat.Button" - android:textSize="@dimen/half_day_text_size"/> + style="@style/TextAppearance.AppCompat.Button"/> @@ -82,6 +80,8 @@ + - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_number_grid.xml b/app/src/main/res/layout/item_number_grid.xml deleted file mode 100644 index 91d8a56..0000000 --- a/app/src/main/res/layout/item_number_grid.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-v21/dimens.xml b/app/src/main/res/values-v21/dimens.xml index ee89ac1..b505a7b 100644 --- a/app/src/main/res/values-v21/dimens.xml +++ b/app/src/main/res/values-v21/dimens.xml @@ -1,7 +1,8 @@ - + 328dp 56dp 16dp + \ No newline at end of file diff --git a/app/src/main/res/values/aosp_datetimepicker_colors.xml b/app/src/main/res/values/aosp_datetimepicker_colors.xml index 1cc249e..f1147c4 100644 --- a/app/src/main/res/values/aosp_datetimepicker_colors.xml +++ b/app/src/main/res/values/aosp_datetimepicker_colors.xml @@ -38,15 +38,13 @@ #ff3333 #853333 - #404040 - #363636 + #424242 + #212121 #808080 #ffffff #888888 #bfbfbf - - #009688 - #76ffffff - #00796b + + #a2ffffff diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 227fd33..ab9a21d 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,7 +2,7 @@ #3F51B5 #303F9F - #FF4081 + #FF4081 #66000000 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 5b0c81e..430df77 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -20,10 +20,18 @@ 300dp 64dp 80dp - 344dp + 344dp 88dp 0dp + + 308dp + 70dp + 56dp + 48dp + 30sp + 28sp + 24dp 16dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 6f9c550..9838fff 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -47,6 +47,24 @@ sans-serif-light + + + +