Skip to content

Commit

Permalink
Add initial TimePickerDialog implementation
Browse files Browse the repository at this point in the history
Issue #6
This is an initial, fully working implementation of the TimePickerDialog
as it is presented in the Google Material Design guidelines. Further
testing and improvements will be made in the future.
  • Loading branch information
taltstidl committed Feb 24, 2016
1 parent 6630cb3 commit 23e177b
Show file tree
Hide file tree
Showing 28 changed files with 3,840 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.format.DateFormat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
Expand All @@ -28,6 +29,8 @@
import com.tr4android.support.extension.internal.Account;
import com.tr4android.support.extension.picker.date.AppCompatDatePicker;
import com.tr4android.support.extension.picker.date.AppCompatDatePickerDialog;
import com.tr4android.support.extension.picker.time.AppCompatTimePicker;
import com.tr4android.support.extension.picker.time.AppCompatTimePickerDialog;
import com.tr4android.support.extension.typeface.TypefaceCompatFactory;
import com.tr4android.support.extension.widget.AccountHeaderView;
import com.tr4android.support.extension.widget.FlexibleToolbarLayout;
Expand Down Expand Up @@ -146,6 +149,8 @@ public void onClick(View view) {
@Override
public void onClick(View view) {
// Show new TimePickerDialog
DialogFragment timePicker = new TimePickerFragment();
timePicker.show(getSupportFragmentManager(), "timePicker");
}
});
}
Expand Down Expand Up @@ -225,4 +230,24 @@ public void onDateSet(AppCompatDatePicker view, int year, int month, int day) {
// Do something with the date chosen by the user
}
}

public static class TimePickerFragment extends DialogFragment
implements AppCompatTimePickerDialog.OnTimeSetListener {

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current time as the default values for the picker
final Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);

// Create a new instance of TimePickerDialog and return it
return new AppCompatTimePickerDialog(getActivity(), this, hour, minute,
false);
}

public void onTimeSet(AppCompatTimePicker view, int hourOfDay, int minute) {
// Do something with the time chosen by the user
}
}
}
8 changes: 7 additions & 1 deletion app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
<item name="colorPrimaryDark">#1976d2</item>
<item name="colorAccent">#3f51b5</item>
<item name="datePickerDialogTheme">@style/DatePickerDialog</item>
<item name="timePickerDialogTheme">@style/TimePickerDialog</item>
</style>

<style name="DatePickerDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<style name="DatePickerDialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">#009688</item>
</style>

<style name="TimePickerDialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">#009688</item>
<item name="numbersBackgroundColor">#ffeeeeee</item>
</style>
</resources>
1 change: 1 addition & 0 deletions appcompat-extension/appcompat-extension.iml
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,6 @@
<orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.1.1" level="project" />
<orderEntry type="library" exported="" name="design-23.1.1" level="project" />
<orderEntry type="library" exported="" name="gridlayout-v7-23.1.1" level="project" />
</component>
</module>
1 change: 1 addition & 0 deletions appcompat-extension/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:design:23.1.1'
compile 'com.android.support:gridlayout-v7:23.1.1'
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.tr4android.support.extension.picker.date;
package com.tr4android.support.extension.picker;

import android.os.Build;
import android.text.format.DateFormat;
Expand All @@ -17,6 +17,8 @@ public static String getBestDateTimePattern(Locale locale, String skeleton) {
// Try to improve the skeleton on older devices
if (skeleton.equals("EMMMd")) return "E, MMM d";
if (skeleton.equals("MMMMy")) return "MMMM yyyy";
if (skeleton.equals("Hm")) return "H:m";
if (skeleton.equals("hm")) return "h:m";
return skeleton;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.tr4android.support.extension.picker;

public class MathUtils {

public static float constrain(float amount, float low, float high) {
return amount < low ? low : (amount > high ? high : amount);
}

public static int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount);
}

public static long constrain(long amount, long low, long high) {
return amount < low ? low : (amount > high ? high : amount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.util.TypedValue;
import android.widget.ImageButton;

import com.tr4android.appcompat.extension.R;
Expand All @@ -39,7 +41,7 @@
* This helper class has methods to easily create all the ColorStateLists needed for
* the AppCompatDatePickerDialog and the AppCompatTimePickerDialog.
*/
public class PickerThemeUtil {
public class PickerThemeUtils {

public static ColorStateList getHeaderTextColorStateList(Context context) {
return new ColorStateList(new int[][]{ // states
Expand All @@ -63,20 +65,53 @@ public static Drawable getHeaderBackground(Context context, @ColorInt int color)
}
}

public static ColorStateList getDayTextColorStateList(Context context) {
public static ColorStateList getTextColorPrimaryActivatedStateList(Context context) {
final float disabledAlpha = getDisabledAlpha(context);
final int textColor = ThemeUtils.getThemeAttrColor(context, android.R.attr.textColorPrimary);
final int textColorActivated = ContextCompat.getColor(context, R.color.abc_primary_text_material_dark);
return new ColorStateList(new int[][]{ // states
new int[]{-android.R.attr.state_enabled, android.R.attr.state_selected},
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_selected},
new int[]{} // state_default
}, new int[]{ // colors
ContextCompat.getColor(context, R.color.abc_secondary_text_material_dark),
ThemeUtils.getThemeAttrColor(context, android.R.attr.textColorSecondary),
ContextCompat.getColor(context, R.color.abc_primary_text_material_dark),
ThemeUtils.getThemeAttrColor(context, android.R.attr.textColorPrimary)
setAlphaComponent(textColorActivated, disabledAlpha),
setAlphaComponent(textColor, disabledAlpha),
textColorActivated,
textColor
});
}

public static ColorStateList getTextColorSecondaryActivatedStateList(Context context) {
final float disabledAlpha = getDisabledAlpha(context);
final int textColor = ThemeUtils.getThemeAttrColor(context, android.R.attr.textColorSecondary);
final int textColorActivated = ContextCompat.getColor(context, R.color.abc_secondary_text_material_dark);
return new ColorStateList(new int[][]{ // states
new int[]{-android.R.attr.state_enabled, android.R.attr.state_selected},
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_selected},
new int[]{} // state_default
}, new int[]{ // colors
setAlphaComponent(textColorActivated, disabledAlpha),
setAlphaComponent(textColor, disabledAlpha),
textColorActivated,
textColor
});
}

public static float getDisabledAlpha(Context context) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
return outValue.getFloat();
}

public static int setAlphaComponent(int color, float alpha) {
final int srcRgb = color & 0xFFFFFF;
final int srcAlpha = (color >> 24) & 0xFF;
final int dstAlpha = (int) (srcAlpha * alpha + 0.5f);
return srcRgb | (dstAlpha << 24);
}

public static ColorStateList getNavButtonColorStateList(Context context) {
return new ColorStateList(new int[][]{ // states
new int[]{android.R.attr.state_pressed},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class CompatUtils {
public class ViewCompatUtils {
public static int getPaddingStart(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return view.getPaddingStart();
Expand Down Expand Up @@ -39,7 +40,7 @@ public static void setTextAppearance(TextView view, int textAppearanceResId) {
}
}

public static void announceForAccessibility(View view, String text) {
public static void announceForAccessibility(View view, CharSequence text) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.announceForAccessibility(text);
} // No-op on versions prior to Jellybean
Expand All @@ -49,15 +50,19 @@ public static boolean isLayoutRtl(View view) {
return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
}

public static float constrain(float amount, float low, float high) {
return amount < low ? low : (amount > high ? high : amount);
}

public static int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount);
public static int getRule(RelativeLayout.LayoutParams params, int verb) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return params.getRule(verb);
} else {
return params.getRules()[verb];
}
}

public static long constrain(long amount, long low, long high) {
return amount < low ? low : (amount > high ? high : amount);
public static void removeRule(RelativeLayout.LayoutParams params, int verb) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
params.removeRule(verb);
} else {
params.addRule(verb, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@
import android.widget.ViewAnimator;

import com.tr4android.appcompat.extension.R;
import com.tr4android.support.extension.picker.CompatUtils;
import com.tr4android.support.extension.picker.PickerThemeUtil;
import com.tr4android.support.extension.picker.DateFormatUtils;
import com.tr4android.support.extension.picker.ViewCompatUtils;
import com.tr4android.support.extension.picker.PickerThemeUtils;
import com.tr4android.support.extension.picker.date.DayPickerView.OnDaySelectedListener;
import com.tr4android.support.extension.picker.date.YearPickerView.OnYearSelectedListener;
import com.tr4android.support.extension.utils.ThemeUtils;
Expand Down Expand Up @@ -123,13 +124,13 @@ public AppCompatDatePickerDelegate(AppCompatDatePicker delegator, Context contex
if (a.hasValue(R.styleable.DatePickerDialog_headerTextColor)) {
headerTextColor = a.getColorStateList(R.styleable.DatePickerDialog_headerTextColor);
} else {
headerTextColor = PickerThemeUtil.getHeaderTextColorStateList(mContext);
headerTextColor = PickerThemeUtils.getHeaderTextColorStateList(mContext);
}
mHeaderYear.setTextColor(headerTextColor);
mHeaderMonthDay.setTextColor(headerTextColor);

// Set up header background, if available.
CompatUtils.setBackground(header, PickerThemeUtil.getHeaderBackground(mContext,
ViewCompatUtils.setBackground(header, PickerThemeUtils.getHeaderBackground(mContext,
a.getColor(R.styleable.DatePickerDialog_headerBackground,
ThemeUtils.getThemeAttrColor(mContext, R.attr.colorAccent))));

Expand Down Expand Up @@ -253,7 +254,7 @@ private void onCurrentDateChanged(boolean announce) {
final long millis = mCurrentDate.getTimeInMillis();
final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
CompatUtils.announceForAccessibility(mAnimator, fullDateText);
ViewCompatUtils.announceForAccessibility(mAnimator, fullDateText);
}
}

Expand All @@ -269,7 +270,7 @@ private void setCurrentView(final int viewIndex) {
mCurrentView = viewIndex;
}

CompatUtils.announceForAccessibility(mAnimator, mSelectDay);
ViewCompatUtils.announceForAccessibility(mAnimator, mSelectDay);
break;
case VIEW_YEAR:
mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
Expand All @@ -281,7 +282,7 @@ private void setCurrentView(final int viewIndex) {
mCurrentView = viewIndex;
}

CompatUtils.announceForAccessibility(mAnimator, mSelectYear);
ViewCompatUtils.announceForAccessibility(mAnimator, mSelectYear);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
Expand All @@ -30,8 +29,9 @@
import android.widget.ImageButton;

import com.tr4android.appcompat.extension.R;
import com.tr4android.support.extension.picker.CompatUtils;
import com.tr4android.support.extension.picker.PickerThemeUtil;
import com.tr4android.support.extension.picker.MathUtils;
import com.tr4android.support.extension.picker.ViewCompatUtils;
import com.tr4android.support.extension.picker.PickerThemeUtils;
import com.tr4android.support.extension.widget.ViewPager;
import com.tr4android.support.extension.widget.ViewPager.OnPageChangeListener;

Expand Down Expand Up @@ -153,8 +153,8 @@ public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyle
mViewPager.addOnPageChangeListener(mOnPageChangedListener);

// Set up background of the previous and next buttons.
CompatUtils.setBackground(mPrevButton, PickerThemeUtil.getNavButtonBackground(context));
CompatUtils.setBackground(mNextButton, PickerThemeUtil.getNavButtonBackground(context));
ViewCompatUtils.setBackground(mPrevButton, PickerThemeUtils.getNavButtonBackground(context));
ViewCompatUtils.setBackground(mNextButton, PickerThemeUtils.getNavButtonBackground(context));

// Set up min and max dates.
final Calendar tempDate = Calendar.getInstance();
Expand All @@ -172,7 +172,7 @@ public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyle
throw new IllegalArgumentException("maxDate must be >= minDate");
}

final long setDateMillis = CompatUtils.constrain(
final long setDateMillis = MathUtils.constrain(
System.currentTimeMillis(), minDateMillis, maxDateMillis);

setFirstDayOfWeek(firstDayOfWeek);
Expand Down Expand Up @@ -226,14 +226,14 @@ public void onRtlPropertiesChanged(int layoutDirection) {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final ImageButton leftButton;
final ImageButton rightButton;
if (CompatUtils.isLayoutRtl(this)) {
if (ViewCompatUtils.isLayoutRtl(this)) {
leftButton = mNextButton;
rightButton = mPrevButton;
} else {
leftButton = mPrevButton;
rightButton = mNextButton;
}
PickerThemeUtil.setNavButtonDrawable(getContext(), leftButton, rightButton,
PickerThemeUtils.setNavButtonDrawable(getContext(), leftButton, rightButton,
mAdapter.getMonthTextAppearance());

final int width = right - left;
Expand Down Expand Up @@ -380,7 +380,7 @@ private int getDiffMonths(Calendar start, Calendar end) {
private int getPositionFromDay(long timeInMillis) {
final int diffMonthMax = getDiffMonths(mMinDate, mMaxDate);
final int diffMonth = getDiffMonths(mMinDate, getTempCalendarForTime(timeInMillis));
return CompatUtils.constrain(diffMonth, 0, diffMonthMax);
return MathUtils.constrain(diffMonth, 0, diffMonthMax);
}

private Calendar getTempCalendarForTime(long timeInMillis) {
Expand Down
Loading

0 comments on commit 23e177b

Please sign in to comment.