Implement reminder time picker; customize picker color

pull/605/head
Alinson S. Xavier 5 years ago
parent 72ad14119a
commit 309b6cbcaf

@ -41,8 +41,8 @@ public class AmPmCirclesView extends View {
private final Paint mPaint = new Paint(); private final Paint mPaint = new Paint();
private int mSelectedAlpha; private int mSelectedAlpha;
private int mUnselectedColor; private int mUnselectedColor;
private int mAmPmTextColor; protected int mAmPmTextColor = Color.WHITE;
private int mSelectedColor; protected int mSelectedColor = Color.BLUE;
private float mCircleRadiusMultiplier; private float mCircleRadiusMultiplier;
private float mAmPmCircleRadiusMultiplier; private float mAmPmCircleRadiusMultiplier;
private String mAmText; private String mAmText;
@ -73,8 +73,8 @@ public class AmPmCirclesView extends View {
Resources res = context.getResources(); Resources res = context.getResources();
mUnselectedColor = res.getColor(R.color.white); mUnselectedColor = res.getColor(R.color.white);
mSelectedColor = res.getColor(R.color.blue); //mSelectedColor = res.getColor(R.color.blue);
mAmPmTextColor = res.getColor(R.color.ampm_text_color); //mAmPmTextColor = res.getColor(R.color.ampm_text_color);
mSelectedAlpha = SELECTED_ALPHA; mSelectedAlpha = SELECTED_ALPHA;
String typefaceFamily = res.getString(R.string.sans_serif); String typefaceFamily = res.getString(R.string.sans_serif);
Typeface tf = Typeface.create(typefaceFamily, Typeface.NORMAL); Typeface tf = Typeface.create(typefaceFamily, Typeface.NORMAL);
@ -105,8 +105,8 @@ public class AmPmCirclesView extends View {
mSelectedAlpha = SELECTED_ALPHA_THEME_DARK; mSelectedAlpha = SELECTED_ALPHA_THEME_DARK;
} else { } else {
mUnselectedColor = res.getColor(R.color.white); mUnselectedColor = res.getColor(R.color.white);
mSelectedColor = res.getColor(R.color.blue); //mSelectedColor = res.getColor(R.color.blue);
mAmPmTextColor = res.getColor(R.color.ampm_text_color); //mAmPmTextColor = res.getColor(R.color.ampm_text_color);
mSelectedAlpha = SELECTED_ALPHA; mSelectedAlpha = SELECTED_ALPHA;
} }
} }

@ -84,6 +84,14 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
private AnimatorSet mTransition; private AnimatorSet mTransition;
private Handler mHandler = new Handler(); private Handler mHandler = new Handler();
public void setColor(int selectedColor)
{
mHourRadialSelectorView.mPaint.setColor(selectedColor);
mMinuteRadialSelectorView.mPaint.setColor(selectedColor);
mAmPmCirclesView.mSelectedColor = selectedColor;
mAmPmCirclesView.mAmPmTextColor = selectedColor;
}
public interface OnValueSelectedListener { public interface OnValueSelectedListener {
void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance); void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance);
} }

@ -40,7 +40,7 @@ public class RadialSelectorView extends View {
// Alpha level for the line. // Alpha level for the line.
private static final int FULL_ALPHA = Utils.FULL_ALPHA; private static final int FULL_ALPHA = Utils.FULL_ALPHA;
private final Paint mPaint = new Paint(); protected final Paint mPaint = new Paint();
private boolean mIsInitialized; private boolean mIsInitialized;
private boolean mDrawValuesReady; private boolean mDrawValuesReady;
@ -96,8 +96,6 @@ public class RadialSelectorView extends View {
Resources res = context.getResources(); Resources res = context.getResources();
int blue = res.getColor(R.color.blue);
mPaint.setColor(blue);
mPaint.setAntiAlias(true); mPaint.setAntiAlias(true);
mSelectionAlpha = SELECTED_ALPHA; mSelectionAlpha = SELECTED_ALPHA;
@ -139,15 +137,11 @@ public class RadialSelectorView extends View {
/* package */ void setTheme(Context context, boolean themeDark) { /* package */ void setTheme(Context context, boolean themeDark) {
Resources res = context.getResources(); Resources res = context.getResources();
int color;
if (themeDark) { if (themeDark) {
color = res.getColor(R.color.red);
mSelectionAlpha = SELECTED_ALPHA_THEME_DARK; mSelectionAlpha = SELECTED_ALPHA_THEME_DARK;
} else { } else {
color = res.getColor(R.color.blue);
mSelectionAlpha = SELECTED_ALPHA; mSelectionAlpha = SELECTED_ALPHA;
} }
mPaint.setColor(color);
} }
/** /**

@ -23,7 +23,9 @@ import android.app.*;
import android.content.*; import android.content.*;
import android.content.res.*; import android.content.res.*;
import android.os.*; import android.os.*;
import androidx.appcompat.app.*; import androidx.appcompat.app.*;
import android.util.*; import android.util.*;
import android.view.*; import android.view.*;
import android.view.View.*; import android.view.View.*;
@ -39,7 +41,8 @@ import java.util.*;
/** /**
* Dialog to set a time. * Dialog to set a time.
*/ */
public class TimePickerDialog extends AppCompatDialogFragment implements OnValueSelectedListener{ public class TimePickerDialog extends AppCompatDialogFragment implements OnValueSelectedListener
{
private static final String TAG = "TimePickerDialog"; private static final String TAG = "TimePickerDialog";
private static final String KEY_HOUR_OF_DAY = "hour_of_day"; private static final String KEY_HOUR_OF_DAY = "hour_of_day";
@ -108,37 +111,50 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* The callback interface used to indicate the user is done filling in * The callback interface used to indicate the user is done filling in
* the time (they clicked on the 'Set' button). * the time (they clicked on the 'Set' button).
*/ */
public interface OnTimeSetListener { public interface OnTimeSetListener
{
/** /**
* @param view The view associated with this listener. * @param view The view associated with this listener.
* @param hourOfDay The hour that was set. * @param hourOfDay The hour that was set.
* @param minute The minute that was set. * @param minute The minute that was set.
*/ */
void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute); void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute);
default void onTimeCleared(RadialPickerLayout view) {} default void onTimeCleared(RadialPickerLayout view)
{
}
} }
public TimePickerDialog() { public TimePickerDialog()
{
// Empty constructor required for dialog fragment. // Empty constructor required for dialog fragment.
} }
@SuppressLint("Java") @SuppressLint("Java")
public TimePickerDialog(Context context, int theme, OnTimeSetListener callback, public TimePickerDialog(Context context, int theme, OnTimeSetListener callback,
int hourOfDay, int minute, boolean is24HourMode) { int hourOfDay, int minute, boolean is24HourMode)
{
// Empty constructor required for dialog fragment. // Empty constructor required for dialog fragment.
} }
public static TimePickerDialog newInstance(OnTimeSetListener callback, public static TimePickerDialog newInstance(OnTimeSetListener callback,
int hourOfDay, int minute, boolean is24HourMode) { int hourOfDay,
int minute,
boolean is24HourMode,
int color)
{
TimePickerDialog ret = new TimePickerDialog(); TimePickerDialog ret = new TimePickerDialog();
ret.initialize(callback, hourOfDay, minute, is24HourMode); ret.initialize(callback, hourOfDay, minute, is24HourMode, color);
return ret; return ret;
} }
public void initialize(OnTimeSetListener callback, public void initialize(OnTimeSetListener callback,
int hourOfDay, int minute, boolean is24HourMode) { int hourOfDay,
int minute,
boolean is24HourMode,
int color)
{
mCallback = callback; mCallback = callback;
mInitialHourOfDay = hourOfDay; mInitialHourOfDay = hourOfDay;
@ -146,35 +162,41 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mIs24HourMode = is24HourMode; mIs24HourMode = is24HourMode;
mInKbMode = false; mInKbMode = false;
mThemeDark = false; mThemeDark = false;
mSelectedColor = color;
} }
/** /**
* Set a dark or light theme. NOTE: this will only take effect for the next onCreateView. * Set a dark or light theme. NOTE: this will only take effect for the next onCreateView.
*/ */
public void setThemeDark(boolean dark) { public void setThemeDark(boolean dark)
{
mThemeDark = dark; mThemeDark = dark;
} }
public boolean isThemeDark() { public boolean isThemeDark()
{
return mThemeDark; return mThemeDark;
} }
public void setOnTimeSetListener(OnTimeSetListener callback) { public void setOnTimeSetListener(OnTimeSetListener callback)
{
mCallback = callback; mCallback = callback;
} }
public void setStartTime(int hourOfDay, int minute) { public void setStartTime(int hourOfDay, int minute)
{
mInitialHourOfDay = hourOfDay; mInitialHourOfDay = hourOfDay;
mInitialMinute = minute; mInitialMinute = minute;
mInKbMode = false; mInKbMode = false;
} }
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_HOUR_OF_DAY) if (savedInstanceState != null && savedInstanceState.containsKey(KEY_HOUR_OF_DAY)
&& savedInstanceState.containsKey(KEY_MINUTE) && savedInstanceState.containsKey(KEY_MINUTE)
&& savedInstanceState.containsKey(KEY_IS_24_HOUR_VIEW)) { && savedInstanceState.containsKey(KEY_IS_24_HOUR_VIEW)) {
mInitialHourOfDay = savedInstanceState.getInt(KEY_HOUR_OF_DAY); mInitialHourOfDay = savedInstanceState.getInt(KEY_HOUR_OF_DAY);
mInitialMinute = savedInstanceState.getInt(KEY_MINUTE); mInitialMinute = savedInstanceState.getInt(KEY_MINUTE);
mIs24HourMode = savedInstanceState.getBoolean(KEY_IS_24_HOUR_VIEW); mIs24HourMode = savedInstanceState.getBoolean(KEY_IS_24_HOUR_VIEW);
@ -191,7 +213,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState)
{
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View view = inflater.inflate(R.layout.time_picker_dialog, null); View view = inflater.inflate(R.layout.time_picker_dialog, null);
@ -203,8 +226,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mSelectHours = res.getString(R.string.select_hours); mSelectHours = res.getString(R.string.select_hours);
mMinutePickerDescription = res.getString(R.string.minute_picker_description); mMinutePickerDescription = res.getString(R.string.minute_picker_description);
mSelectMinutes = res.getString(R.string.select_minutes); mSelectMinutes = res.getString(R.string.select_minutes);
mSelectedColor = res.getColor(mThemeDark? R.color.red : R.color.blue); //mSelectedColor = res.getColor(mThemeDark ? R.color.red : R.color.blue);
mUnselectedColor = res.getColor(mThemeDark? R.color.white : R.color.numbers_text_color); mUnselectedColor = res.getColor(mThemeDark ? R.color.white : R.color.numbers_text_color);
mHourView = (TextView) view.findViewById(R.id.hours); mHourView = (TextView) view.findViewById(R.id.hours);
mHourView.setOnKeyListener(keyboardListener); mHourView.setOnKeyListener(keyboardListener);
@ -223,8 +246,9 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mTimePicker = (RadialPickerLayout) view.findViewById(R.id.time_picker); mTimePicker = (RadialPickerLayout) view.findViewById(R.id.time_picker);
mTimePicker.setOnValueSelectedListener(this); mTimePicker.setOnValueSelectedListener(this);
mTimePicker.setOnKeyListener(keyboardListener); mTimePicker.setOnKeyListener(keyboardListener);
mTimePicker.setColor(mSelectedColor);
mTimePicker.initialize(getActivity(), mHapticFeedbackController, mInitialHourOfDay, mTimePicker.initialize(getActivity(), mHapticFeedbackController, mInitialHourOfDay,
mInitialMinute, mIs24HourMode); mInitialMinute, mIs24HourMode);
int currentItemShowing = HOUR_INDEX; int currentItemShowing = HOUR_INDEX;
if (savedInstanceState != null && if (savedInstanceState != null &&
@ -234,25 +258,31 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
setCurrentItemShowing(currentItemShowing, false, true, true); setCurrentItemShowing(currentItemShowing, false, true, true);
mTimePicker.invalidate(); mTimePicker.invalidate();
mHourView.setOnClickListener(new OnClickListener() { mHourView.setOnClickListener(new OnClickListener()
{
@Override @Override
public void onClick(View v) { public void onClick(View v)
{
setCurrentItemShowing(HOUR_INDEX, true, false, true); setCurrentItemShowing(HOUR_INDEX, true, false, true);
tryVibrate(); tryVibrate();
} }
}); });
mMinuteView.setOnClickListener(new OnClickListener() { mMinuteView.setOnClickListener(new OnClickListener()
{
@Override @Override
public void onClick(View v) { public void onClick(View v)
{
setCurrentItemShowing(MINUTE_INDEX, true, false, true); setCurrentItemShowing(MINUTE_INDEX, true, false, true);
tryVibrate(); tryVibrate();
} }
}); });
mDoneButton = (TextView) view.findViewById(R.id.done_button); mDoneButton = (TextView) view.findViewById(R.id.done_button);
mDoneButton.setOnClickListener(new OnClickListener() { mDoneButton.setOnClickListener(new OnClickListener()
{
@Override @Override
public void onClick(View v) { public void onClick(View v)
{
if (mInKbMode && isTypedTimeFullyLegal()) { if (mInKbMode && isTypedTimeFullyLegal()) {
finishKbMode(false); finishKbMode(false);
} else { } else {
@ -260,7 +290,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
if (mCallback != null) { if (mCallback != null) {
mCallback.onTimeSet(mTimePicker, mCallback.onTimeSet(mTimePicker,
mTimePicker.getHours(), mTimePicker.getMinutes()); mTimePicker.getHours(), mTimePicker.getMinutes());
} }
dismiss(); dismiss();
} }
@ -269,16 +299,16 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mClearButton = (TextView) view.findViewById(R.id.clear_button); mClearButton = (TextView) view.findViewById(R.id.clear_button);
mClearButton.setOnClickListener(new OnClickListener() mClearButton.setOnClickListener(new OnClickListener()
{ {
@Override @Override
public void onClick(View v) public void onClick(View v)
{ {
if(mCallback != null) { if (mCallback != null) {
mCallback.onTimeCleared(mTimePicker); mCallback.onTimeCleared(mTimePicker);
} }
dismiss(); dismiss();
} }
}); });
mClearButton.setOnKeyListener(keyboardListener); mClearButton.setOnKeyListener(keyboardListener);
// Enable or disable the AM/PM view. // Enable or disable the AM/PM view.
@ -293,15 +323,17 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
separatorView.setLayoutParams(paramsSeparator); separatorView.setLayoutParams(paramsSeparator);
} else { } else {
mAmPmTextView.setVisibility(View.VISIBLE); mAmPmTextView.setVisibility(View.VISIBLE);
updateAmPmDisplay(mInitialHourOfDay < 12? AM : PM); updateAmPmDisplay(mInitialHourOfDay < 12 ? AM : PM);
mAmPmHitspace.setOnClickListener(new OnClickListener() { mAmPmHitspace.setOnClickListener(new OnClickListener()
{
@Override @Override
public void onClick(View v) { public void onClick(View v)
{
tryVibrate(); tryVibrate();
int amOrPm = mTimePicker.getIsCurrentlyAmOrPm(); int amOrPm = mTimePicker.getIsCurrentlyAmOrPm();
if (amOrPm == AM) { if (amOrPm == AM) {
amOrPm = PM; amOrPm = PM;
} else if (amOrPm == PM){ } else if (amOrPm == PM) {
amOrPm = AM; amOrPm = AM;
} }
updateAmPmDisplay(amOrPm); updateAmPmDisplay(amOrPm);
@ -328,56 +360,61 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mTypedTimes = new ArrayList<Integer>(); mTypedTimes = new ArrayList<Integer>();
} }
// Set the theme at the end so that the initialize()s above don't counteract the theme.
mTimePicker.setTheme(getActivity().getApplicationContext(), mThemeDark);
// Prepare some palette to use.
int white = res.getColor(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);
ColorStateList doneTextColor = res.getColorStateList(R.color.done_text_color);
int doneBackground = R.drawable.done_background_color;
int darkGray = res.getColor(R.color.dark_gray); // // Set the theme at the end so that the initialize()s above don't counteract the theme.
int lightGray = res.getColor(R.color.light_gray); // mTimePicker.setTheme(getActivity().getApplicationContext(), mThemeDark);
int darkLine = res.getColor(R.color.line_dark); // // Prepare some palette to use.
ColorStateList darkDoneTextColor = res.getColorStateList(R.color.done_text_color_dark); // int white = res.getColor(R.color.white);
int darkDoneBackground = R.drawable.done_background_color_dark; // 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);
// 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);
// ColorStateList darkDoneTextColor = res.getColorStateList(R.color.done_text_color_dark);
// int darkDoneBackground = R.drawable.done_background_color_dark;
// Set the palette for each view based on the theme. // Set the palette for each view based on the theme.
view.findViewById(R.id.time_display_background).setBackgroundColor(mThemeDark? darkGray : white); // view.findViewById(R.id.time_display_background).setBackgroundColor(mThemeDark? darkGray : white);
view.findViewById(R.id.time_display).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.separator)).setTextColor(mThemeDark? white : timeDisplay);
((TextView) view.findViewById(R.id.ampm_label)).setTextColor(mThemeDark? white : timeDisplay); // ((TextView) view.findViewById(R.id.ampm_label)).setTextColor(mThemeDark? white : timeDisplay);
view.findViewById(R.id.line).setBackgroundColor(mThemeDark? darkLine : line); // view.findViewById(R.id.line).setBackgroundColor(mThemeDark? darkLine : line);
mDoneButton.setTextColor(mThemeDark? darkDoneTextColor : doneTextColor); // mDoneButton.setTextColor(mThemeDark? darkDoneTextColor : doneTextColor);
mTimePicker.setBackgroundColor(mThemeDark? lightGray : circleBackground); // mTimePicker.setBackgroundColor(mThemeDark? lightGray : circleBackground);
mDoneButton.setBackgroundResource(mThemeDark? darkDoneBackground : doneBackground); // mDoneButton.setBackgroundResource(mThemeDark? darkDoneBackground : doneBackground);
return view; return view;
} }
@Override @Override
public void onResume() { public void onResume()
{
super.onResume(); super.onResume();
mHapticFeedbackController.start(); mHapticFeedbackController.start();
} }
@Override @Override
public void onPause() { public void onPause()
{
super.onPause(); super.onPause();
mHapticFeedbackController.stop(); mHapticFeedbackController.stop();
} }
public void tryVibrate() { public void tryVibrate()
{
mHapticFeedbackController.tryVibrate(); mHapticFeedbackController.tryVibrate();
} }
private void updateAmPmDisplay(int amOrPm) { private void updateAmPmDisplay(int amOrPm)
{
if (amOrPm == AM) { if (amOrPm == AM) {
mAmPmTextView.setText(mAmText); mAmPmTextView.setText(mAmText);
Utils.tryAccessibilityAnnounce(mTimePicker, mAmText); Utils.tryAccessibilityAnnounce(mTimePicker, mAmText);
mAmPmHitspace.setContentDescription(mAmText); mAmPmHitspace.setContentDescription(mAmText);
} else if (amOrPm == PM){ } else if (amOrPm == PM) {
mAmPmTextView.setText(mPmText); mAmPmTextView.setText(mPmText);
Utils.tryAccessibilityAnnounce(mTimePicker, mPmText); Utils.tryAccessibilityAnnounce(mTimePicker, mPmText);
mAmPmHitspace.setContentDescription(mPmText); mAmPmHitspace.setContentDescription(mPmText);
@ -387,7 +424,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState)
{
if (mTimePicker != null) { if (mTimePicker != null) {
outState.putInt(KEY_HOUR_OF_DAY, mTimePicker.getHours()); outState.putInt(KEY_HOUR_OF_DAY, mTimePicker.getHours());
outState.putInt(KEY_MINUTE, mTimePicker.getMinutes()); outState.putInt(KEY_MINUTE, mTimePicker.getMinutes());
@ -405,7 +443,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* Called by the picker for updating the header display. * Called by the picker for updating the header display.
*/ */
@Override @Override
public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) { public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance)
{
if (pickerIndex == HOUR_INDEX) { if (pickerIndex == HOUR_INDEX) {
setHour(newValue, false); setHour(newValue, false);
String announcement = String.format("%d", newValue); String announcement = String.format("%d", newValue);
@ -417,7 +456,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
Utils.tryAccessibilityAnnounce(mTimePicker, announcement); Utils.tryAccessibilityAnnounce(mTimePicker, announcement);
} else if (pickerIndex == MINUTE_INDEX){ } else if (pickerIndex == MINUTE_INDEX) {
setMinute(newValue); setMinute(newValue);
mTimePicker.setContentDescription(mMinutePickerDescription + ": " + newValue); mTimePicker.setContentDescription(mMinutePickerDescription + ": " + newValue);
} else if (pickerIndex == AMPM_INDEX) { } else if (pickerIndex == AMPM_INDEX) {
@ -430,7 +469,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
private void setHour(int value, boolean announce) { private void setHour(int value, boolean announce)
{
String format; String format;
if (mIs24HourMode) { if (mIs24HourMode) {
format = "%02d"; format = "%02d";
@ -450,7 +490,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
private void setMinute(int value) { private void setMinute(int value)
{
if (value == 60) { if (value == 60) {
value = 0; value = 0;
} }
@ -462,7 +503,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
// Show either Hours or Minutes. // Show either Hours or Minutes.
private void setCurrentItemShowing(int index, boolean animateCircle, boolean delayLabelAnimate, private void setCurrentItemShowing(int index, boolean animateCircle, boolean delayLabelAnimate,
boolean announce) { boolean announce)
{
mTimePicker.setCurrentItemShowing(index, animateCircle); mTimePicker.setCurrentItemShowing(index, animateCircle);
TextView labelToAnimate; TextView labelToAnimate;
@ -485,8 +527,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
labelToAnimate = mMinuteView; labelToAnimate = mMinuteView;
} }
int hourColor = (index == HOUR_INDEX)? mSelectedColor : mUnselectedColor; int hourColor = (index == HOUR_INDEX) ? mSelectedColor : mUnselectedColor;
int minuteColor = (index == MINUTE_INDEX)? mSelectedColor : mUnselectedColor; int minuteColor = (index == MINUTE_INDEX) ? mSelectedColor : mUnselectedColor;
mHourView.setTextColor(hourColor); mHourView.setTextColor(hourColor);
mMinuteView.setTextColor(minuteColor); mMinuteView.setTextColor(minuteColor);
@ -499,15 +541,17 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* For keyboard mode, processes key events. * For keyboard mode, processes key events.
*
* @param keyCode the pressed key. * @param keyCode the pressed key.
* @return true if the key was successfully processed, false otherwise. * @return true if the key was successfully processed, false otherwise.
*/ */
private boolean processKeyUp(int keyCode) { private boolean processKeyUp(int keyCode)
{
if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_BACK) {
dismiss(); dismiss();
return true; return true;
} else if (keyCode == KeyEvent.KEYCODE_TAB) { } else if (keyCode == KeyEvent.KEYCODE_TAB) {
if(mInKbMode) { if (mInKbMode) {
if (isTypedTimeFullyLegal()) { if (isTypedTimeFullyLegal()) {
finishKbMode(true); finishKbMode(true);
} }
@ -522,7 +566,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
if (mCallback != null) { if (mCallback != null) {
mCallback.onTimeSet(mTimePicker, mCallback.onTimeSet(mTimePicker,
mTimePicker.getHours(), mTimePicker.getMinutes()); mTimePicker.getHours(), mTimePicker.getMinutes());
} }
dismiss(); dismiss();
return true; return true;
@ -539,7 +583,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
deletedKeyStr = String.format("%d", getValFromKeyCode(deleted)); deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
} }
Utils.tryAccessibilityAnnounce(mTimePicker, Utils.tryAccessibilityAnnounce(mTimePicker,
String.format(mDeletedKeyFormat, deletedKeyStr)); String.format(mDeletedKeyFormat, deletedKeyStr));
updateDisplay(true); updateDisplay(true);
} }
} }
@ -549,7 +593,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
|| keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7 || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
|| keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9 || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9
|| (!mIs24HourMode && || (!mIs24HourMode &&
(keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) { (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
if (!mInKbMode) { if (!mInKbMode) {
if (mTimePicker == null) { if (mTimePicker == null) {
// Something's wrong, because time picker should definitely not be null. // Something's wrong, because time picker should definitely not be null.
@ -572,11 +616,13 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* Try to start keyboard mode with the specified key, as long as the timepicker is not in the * Try to start keyboard mode with the specified key, as long as the timepicker is not in the
* middle of a touch-event. * middle of a touch-event.
*
* @param keyCode The key to use as the first press. Keyboard mode will not be started if the * @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 is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
* key. * key.
*/ */
private void tryStartingKbMode(int keyCode) { private void tryStartingKbMode(int keyCode)
{
if (mTimePicker.trySettingInputEnabled(false) && if (mTimePicker.trySettingInputEnabled(false) &&
(keyCode == -1 || addKeyIfLegal(keyCode))) { (keyCode == -1 || addKeyIfLegal(keyCode))) {
mInKbMode = true; mInKbMode = true;
@ -585,7 +631,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
private boolean addKeyIfLegal(int keyCode) { 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, // 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. // we'll need to see if AM/PM have been typed.
if ((mIs24HourMode && mTypedTimes.size() == 4) || if ((mIs24HourMode && mTypedTimes.size() == 4) ||
@ -617,7 +664,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* Traverse the tree to see if the keys that have been typed so far are legal as is, * 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). * or may become legal as more keys are typed (excluding backspace).
*/ */
private boolean isTypedTimeLegalSoFar() { private boolean isTypedTimeLegalSoFar()
{
Node node = mLegalTimesTree; Node node = mLegalTimesTree;
for (int keyCode : mTypedTimes) { for (int keyCode : mTypedTimes) {
node = node.canReach(keyCode); node = node.canReach(keyCode);
@ -631,7 +679,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* Check if the time that has been typed so far is completely legal, as is. * Check if the time that has been typed so far is completely legal, as is.
*/ */
private boolean isTypedTimeFullyLegal() { private boolean isTypedTimeFullyLegal()
{
if (mIs24HourMode) { if (mIs24HourMode) {
// For 24-hour mode, the time is legal if the hours and minutes are each legal. Note: // 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. // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode.
@ -645,7 +694,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
private int deleteLastTypedKey() { private int deleteLastTypedKey()
{
int deleted = mTypedTimes.remove(mTypedTimes.size() - 1); int deleted = mTypedTimes.remove(mTypedTimes.size() - 1);
if (!isTypedTimeFullyLegal()) { if (!isTypedTimeFullyLegal()) {
mDoneButton.setEnabled(false); mDoneButton.setEnabled(false);
@ -655,9 +705,11 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time. * 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. * @param changeDisplays If true, update the displays with the relevant time.
*/ */
private void finishKbMode(boolean updateDisplays) { private void finishKbMode(boolean updateDisplays)
{
mInKbMode = false; mInKbMode = false;
if (!mTypedTimes.isEmpty()) { if (!mTypedTimes.isEmpty()) {
int values[] = getEnteredTime(null); int values[] = getEnteredTime(null);
@ -677,29 +729,31 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is * 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 * empty, either show an empty display (filled with the placeholder text), or update from the
* timepicker's values. * timepicker's values.
*
* @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text. * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text.
* Otherwise, revert to the timepicker's values. * Otherwise, revert to the timepicker's values.
*/ */
private void updateDisplay(boolean allowEmptyDisplay) { private void updateDisplay(boolean allowEmptyDisplay)
{
if (!allowEmptyDisplay && mTypedTimes.isEmpty()) { if (!allowEmptyDisplay && mTypedTimes.isEmpty()) {
int hour = mTimePicker.getHours(); int hour = mTimePicker.getHours();
int minute = mTimePicker.getMinutes(); int minute = mTimePicker.getMinutes();
setHour(hour, true); setHour(hour, true);
setMinute(minute); setMinute(minute);
if (!mIs24HourMode) { if (!mIs24HourMode) {
updateAmPmDisplay(hour < 12? AM : PM); updateAmPmDisplay(hour < 12 ? AM : PM);
} }
setCurrentItemShowing(mTimePicker.getCurrentItemShowing(), true, true, true); setCurrentItemShowing(mTimePicker.getCurrentItemShowing(), true, true, true);
mDoneButton.setEnabled(true); mDoneButton.setEnabled(true);
} else { } else {
Boolean[] enteredZeros = {false, false}; Boolean[] enteredZeros = {false, false};
int[] values = getEnteredTime(enteredZeros); int[] values = getEnteredTime(enteredZeros);
String hourFormat = enteredZeros[0]? "%02d" : "%2d"; String hourFormat = enteredZeros[0] ? "%02d" : "%2d";
String minuteFormat = (enteredZeros[1])? "%02d" : "%2d"; String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d";
String hourStr = (values[0] == -1)? mDoublePlaceholderText : String hourStr = (values[0] == -1) ? mDoublePlaceholderText :
String.format(hourFormat, values[0]).replace(' ', mPlaceholderText); String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
String minuteStr = (values[1] == -1)? mDoublePlaceholderText : String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText); String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
mHourView.setText(hourStr); mHourView.setText(hourStr);
mHourSpaceView.setText(hourStr); mHourSpaceView.setText(hourStr);
mHourView.setTextColor(mUnselectedColor); mHourView.setTextColor(mUnselectedColor);
@ -712,7 +766,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
private static int getValFromKeyCode(int keyCode) { private static int getValFromKeyCode(int keyCode)
{
switch (keyCode) { switch (keyCode) {
case KeyEvent.KEYCODE_0: case KeyEvent.KEYCODE_0:
return 0; return 0;
@ -741,20 +796,22 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* Get the currently-entered time, as integer values of the hours and minutes typed. * 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 * @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 * 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. * 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 * @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. * minutes, and the third will be either TimePickerDialog.AM or TimePickerDialog.PM.
*/ */
private int[] getEnteredTime(Boolean[] enteredZeros) { private int[] getEnteredTime(Boolean[] enteredZeros)
{
int amOrPm = -1; int amOrPm = -1;
int startIndex = 1; int startIndex = 1;
if (!mIs24HourMode && isTypedTimeFullyLegal()) { if (!mIs24HourMode && isTypedTimeFullyLegal()) {
int keyCode = mTypedTimes.get(mTypedTimes.size() - 1); int keyCode = mTypedTimes.get(mTypedTimes.size() - 1);
if (keyCode == getAmOrPmKeyCode(AM)) { if (keyCode == getAmOrPmKeyCode(AM)) {
amOrPm = AM; amOrPm = AM;
} else if (keyCode == getAmOrPmKeyCode(PM)){ } else if (keyCode == getAmOrPmKeyCode(PM)) {
amOrPm = PM; amOrPm = PM;
} }
startIndex = 2; startIndex = 2;
@ -765,15 +822,15 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i)); int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i));
if (i == startIndex) { if (i == startIndex) {
minute = val; minute = val;
} else if (i == startIndex+1) { } else if (i == startIndex + 1) {
minute += 10*val; minute += 10 * val;
if (enteredZeros != null && val == 0) { if (enteredZeros != null && val == 0) {
enteredZeros[1] = true; enteredZeros[1] = true;
} }
} else if (i == startIndex+2) { } else if (i == startIndex + 2) {
hour = val; hour = val;
} else if (i == startIndex+3) { } else if (i == startIndex + 3) {
hour += 10*val; hour += 10 * val;
if (enteredZeros != null && val == 0) { if (enteredZeros != null && val == 0) {
enteredZeros[0] = true; enteredZeros[0] = true;
} }
@ -787,7 +844,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* Get the keycode value for AM and PM in the current language. * Get the keycode value for AM and PM in the current language.
*/ */
private int getAmOrPmKeyCode(int amOrPm) { private int getAmOrPmKeyCode(int amOrPm)
{
// Cache the codes. // Cache the codes.
if (mAmKeyCode == -1 || mPmKeyCode == -1) { if (mAmKeyCode == -1 || mPmKeyCode == -1) {
// Find the first character in the AM/PM text that is unique. // Find the first character in the AM/PM text that is unique.
@ -822,7 +880,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/** /**
* Create a tree for deciding what keys can legally be typed. * Create a tree for deciding what keys can legally be typed.
*/ */
private void generateLegalTimesTree() { private void generateLegalTimesTree()
{
// Create a quick cache of numbers to their keycodes. // Create a quick cache of numbers to their keycodes.
int k0 = KeyEvent.KEYCODE_0; int k0 = KeyEvent.KEYCODE_0;
int k1 = KeyEvent.KEYCODE_1; int k1 = KeyEvent.KEYCODE_1;
@ -955,20 +1014,24 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* mLegalKeys represents the keys that can be typed to get to the node. * mLegalKeys represents the keys that can be typed to get to the node.
* mChildren are the children that can be reached from this node. * mChildren are the children that can be reached from this node.
*/ */
private class Node { private class Node
{
private int[] mLegalKeys; private int[] mLegalKeys;
private ArrayList<Node> mChildren; private ArrayList<Node> mChildren;
public Node(int... legalKeys) { public Node(int... legalKeys)
{
mLegalKeys = legalKeys; mLegalKeys = legalKeys;
mChildren = new ArrayList<Node>(); mChildren = new ArrayList<Node>();
} }
public void addChild(Node child) { public void addChild(Node child)
{
mChildren.add(child); mChildren.add(child);
} }
public boolean containsKey(int key) { public boolean containsKey(int key)
{
for (int i = 0; i < mLegalKeys.length; i++) { for (int i = 0; i < mLegalKeys.length; i++) {
if (mLegalKeys[i] == key) { if (mLegalKeys[i] == key) {
return true; return true;
@ -977,7 +1040,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
return false; return false;
} }
public Node canReach(int key) { public Node canReach(int key)
{
if (mChildren == null) { if (mChildren == null) {
return null; return null;
} }
@ -990,9 +1054,11 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
private class KeyboardListener implements OnKeyListener { private class KeyboardListener implements OnKeyListener
{
@Override @Override
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_UP) { if (event.getAction() == KeyEvent.ACTION_UP) {
return processKeyUp(keyCode); return processKeyUp(keyCode);
} }
@ -1000,14 +1066,16 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
} }
} }
public void setDismissListener( DialogInterface.OnDismissListener listener ) { public void setDismissListener(DialogInterface.OnDismissListener listener)
{
dismissListener = listener; dismissListener = listener;
} }
@Override @Override
public void onDismiss(DialogInterface dialog) { public void onDismiss(DialogInterface dialog)
{
super.onDismiss(dialog); super.onDismiss(dialog);
if( dismissListener != null ) if (dismissListener != null)
dismissListener.onDismiss(dialog); dismissListener.onDismiss(dialog);
} }
} }

@ -49,33 +49,33 @@
android:layout_height="1dip" android:layout_height="1dip"
android:background="@color/line_background" /> android:background="@color/line_background" />
<LinearLayout <androidx.appcompat.widget.LinearLayoutCompat
style="?android:attr/buttonBarStyle" style="?android:attr/buttonBarStyle"
android:layout_width="@dimen/date_picker_component_width" android:layout_width="@dimen/date_picker_component_width"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" > android:orientation="horizontal" >
<Button <androidx.appcompat.widget.AppCompatButton
style="?android:attr/buttonBarButtonStyle"
android:id="@+id/clear_button" android:id="@+id/clear_button"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/done_background_color"
android:minHeight="48dp" android:minHeight="48dp"
android:textColor="#333"
android:text="@string/clear_label" android:text="@string/clear_label"
android:textColor="@color/done_text_color"
android:textSize="@dimen/done_label_size" /> android:textSize="@dimen/done_label_size" />
<Button <androidx.appcompat.widget.AppCompatButton
style="?android:attr/buttonBarButtonStyle"
android:id="@+id/done_button" android:id="@+id/done_button"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/done_background_color"
android:minHeight="48dp" android:minHeight="48dp"
android:textColor="#333"
android:text="@string/done_label" android:text="@string/done_label"
android:textColor="@color/done_text_color"
android:textSize="@dimen/done_label_size" /> android:textSize="@dimen/done_label_size" />
</LinearLayout> </androidx.appcompat.widget.LinearLayoutCompat>
</LinearLayout> </LinearLayout>

@ -22,8 +22,11 @@ package org.isoron.uhabits.activities.habits.edit
import android.content.res.* import android.content.res.*
import android.graphics.* import android.graphics.*
import android.os.* import android.os.*
import android.text.format.*
import androidx.appcompat.app.* import androidx.appcompat.app.*
import com.android.datetimepicker.time.*
import org.isoron.androidbase.utils.* import org.isoron.androidbase.utils.*
import org.isoron.uhabits.*
import org.isoron.uhabits.activities.* import org.isoron.uhabits.activities.*
import org.isoron.uhabits.activities.common.dialogs.* import org.isoron.uhabits.activities.common.dialogs.*
import org.isoron.uhabits.core.preferences.* import org.isoron.uhabits.core.preferences.*
@ -38,8 +41,12 @@ class EditHabitActivity : AppCompatActivity() {
private lateinit var binding: ActivityEditHabitBinding private lateinit var binding: ActivityEditHabitBinding
var paletteColor = 11 var paletteColor = 11
var androidColor = 0
var freqNum = 1 var freqNum = 1
var freqDen = 1 var freqDen = 1
var reminderHour = -1
var reminderMin = -1
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -69,7 +76,7 @@ class EditHabitActivity : AppCompatActivity() {
populateFrequency() populateFrequency()
binding.frequencyPicker.setOnClickListener { binding.frequencyPicker.setOnClickListener {
val dialog = FrequencyPickerDialog(freqNum, freqDen) val dialog = FrequencyPickerDialog(freqNum, freqDen)
dialog.onFrequencyPicked = {num, den -> dialog.onFrequencyPicked = { num, den ->
freqNum = num freqNum = num
freqDen = den freqDen = den
populateFrequency() populateFrequency()
@ -77,27 +84,55 @@ class EditHabitActivity : AppCompatActivity() {
dialog.show(supportFragmentManager, "frequencyPicker") dialog.show(supportFragmentManager, "frequencyPicker")
} }
binding.reminderTimePicker.setOnClickListener {
val currentHour = if (reminderHour >= 0) reminderHour else 8
val currentMin = if (reminderMin >= 0) reminderMin else 0
val is24HourMode = DateFormat.is24HourFormat(this)
val dialog = TimePickerDialog.newInstance(object : TimePickerDialog.OnTimeSetListener {
override fun onTimeSet(view: RadialPickerLayout?, hourOfDay: Int, minute: Int) {
reminderHour = hourOfDay
reminderMin = minute
populateReminder()
}
override fun onTimeCleared(view: RadialPickerLayout?) {
reminderHour = -1
reminderMin = -1
populateReminder()
}
}, currentHour, currentMin, is24HourMode, androidColor)
dialog.show(supportFragmentManager, "timePicker")
}
binding.buttonSave.setOnClickListener { binding.buttonSave.setOnClickListener {
finish() finish()
} }
} }
private fun populateReminder() {
if (reminderHour < 0) {
binding.reminderTimePicker.text = getString(R.string.reminder_off)
} else {
val time = AndroidDateUtils.formatTime(this, reminderHour, reminderMin)
binding.reminderTimePicker.text = time
}
}
private fun populateFrequency() { private fun populateFrequency() {
val label = when { val label = when {
freqNum == 1 && freqDen == 1 -> "Every day" freqNum == 1 && freqDen == 1 -> getString(R.string.every_day)
freqNum == 1 && freqDen == 7 -> "Every week" freqNum == 1 && freqDen == 7 -> getString(R.string.every_week)
freqNum == 1 && freqDen > 1 -> "Every $freqDen days" freqNum == 1 && freqDen > 1 -> getString(R.string.every_x_days, freqDen)
freqDen == 7 -> "$freqNum times per week" freqDen == 7 -> getString(R.string.x_times_per_week, freqNum)
freqDen == 31 -> "$freqNum times per month" freqDen == 31 -> getString(R.string.x_times_per_month, freqNum)
else -> "Unknown" else -> "Unknown"
} }
binding.frequencyPicker.text = label binding.frequencyPicker.text = label
} }
private fun updateColors() { private fun updateColors() {
val androidColor = PaletteUtils.getColor(this, paletteColor) androidColor = PaletteUtils.getColor(this, paletteColor)
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor) binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
if(!themeSwitcher.isNightMode) { if (!themeSwitcher.isNightMode) {
val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f) val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f)
window.statusBarColor = darkerAndroidColor window.statusBarColor = darkerAndroidColor
binding.toolbar.setBackgroundColor(androidColor) binding.toolbar.setBackgroundColor(androidColor)

@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.edit;
import android.app.*; import android.app.*;
import android.content.*; import android.content.*;
import android.graphics.*;
import android.os.*; import android.os.*;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -265,7 +266,7 @@ public class EditHabitDialog extends AppCompatDialogFragment
boolean is24HourMode = DateFormat.is24HourFormat(getContext()); boolean is24HourMode = DateFormat.is24HourFormat(getContext());
timePicker = timePicker =
TimePickerDialog.newInstance(reminderPanel, currentHour, TimePickerDialog.newInstance(reminderPanel, currentHour,
currentMin, is24HourMode); currentMin, is24HourMode, Color.BLUE);
timePicker.show(getFragmentManager(), "timePicker"); timePicker.show(getFragmentManager(), "timePicker");
} }

@ -2,6 +2,7 @@ package org.isoron.uhabits.notifications;
import android.app.*; import android.app.*;
import android.graphics.*;
import android.os.*; import android.os.*;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -66,7 +67,8 @@ public class SnoozeDelayPickerActivity extends FragmentActivity
}, },
calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE), calendar.get(Calendar.MINUTE),
DateFormat.is24HourFormat(this)); DateFormat.is24HourFormat(this),
Color.BLUE);
dialog.show(getSupportFragmentManager(), "timePicker"); dialog.show(getSupportFragmentManager(), "timePicker");
} }

@ -1,142 +1,142 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:orientation="vertical" android:orientation="vertical"
tools:context=".activities.habits.edit.EditHabitActivity"> tools:context=".activities.habits.edit.EditHabitActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:elevation="2dp" android:elevation="2dp"
android:gravity="end" android:gravity="end"
android:minHeight="?attr/actionBarSize" android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:title="@string/create_habit" app:title="@string/create_habit"
app:titleTextColor="@color/white"> app:titleTextColor="@color/white">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/buttonSave" android:id="@+id/buttonSave"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:text="@string/save" android:text="@string/save"
android:textColor="@color/white" android:textColor="@color/white"
app:rippleColor="@color/white" app:rippleColor="@color/white"
app:strokeColor="@color/white" /> app:strokeColor="@color/white" />
</androidx.appcompat.widget.Toolbar> </androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="8dp" android:orientation="vertical"
android:orientation="vertical"> android:paddingTop="8dp">
<!-- Title and color --> <!-- Title and color -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<!-- Habit Title --> <!-- Habit Title -->
<FrameLayout <FrameLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:background="@drawable/bg_input_group"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-17dp" android:layout_marginTop="-17dp"
android:layout_marginBottom="-4dp" android:layout_marginBottom="-4dp"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:textSize="@dimen/smallTextSize" android:text="@string/name"
android:text="@string/name" /> android:textSize="@dimen/smallTextSize" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/transparent" android:background="@color/transparent"
android:textSize="@dimen/regularTextSize" android:hint="e.g. Exercise"
android:padding="16dp" android:padding="16dp"
android:hint="e.g. Exercise" /> android:textSize="@dimen/regularTextSize" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<!-- Habit Color --> <!-- Habit Color -->
<FrameLayout <FrameLayout
android:layout_width="80dp" android:layout_width="80dp"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="8dp"
android:paddingStart="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingStart="0dp" android:background="@drawable/bg_input_group"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_input_group"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-17dp" android:layout_marginTop="-17dp"
android:layout_marginBottom="-4dp" android:layout_marginBottom="-4dp"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:textSize="@dimen/smallTextSize" android:text="Color"
android:text="Color" /> android:textSize="@dimen/smallTextSize" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/colorButton" android:id="@+id/colorButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" android:layout_marginTop="8dp"
android:layout_marginTop="8dp" android:layout_marginRight="16dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:backgroundTint="#E23673"/> android:backgroundTint="#E23673" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
@ -147,166 +147,166 @@
<!-- Habit Question --> <!-- Habit Question -->
<FrameLayout <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="8dp"
android:paddingTop="4dp">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp"
android:paddingTop="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-17dp" android:layout_marginTop="-17dp"
android:layout_marginBottom="-4dp" android:layout_marginBottom="-4dp"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:textSize="@dimen/smallTextSize" android:text="@string/question"
android:text="@string/question" /> android:textSize="@dimen/smallTextSize" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/transparent" android:background="@color/transparent"
android:textSize="@dimen/regularTextSize" android:hint="@string/example_question_boolean"
android:hint="@string/example_question_boolean" android:padding="16dp"
android:padding="16dp" android:text=""
android:text="" /> android:textSize="@dimen/regularTextSize" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<!-- Frequency --> <!-- Frequency -->
<FrameLayout <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="8dp"
android:paddingTop="4dp">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp"
android:paddingTop="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-17dp" android:layout_marginTop="-17dp"
android:layout_marginBottom="-4dp" android:layout_marginBottom="-4dp"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:textSize="@dimen/smallTextSize" android:paddingStart="8dp"
android:paddingStart="8dp" android:paddingEnd="8dp"
android:paddingEnd="8dp" android:text="@string/frequency"
android:text="@string/frequency" /> android:textSize="@dimen/smallTextSize" />
<TextView <TextView
android:id="@+id/frequencyPicker" android:id="@+id/frequencyPicker"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_arrow_drop_down_dark" android:drawableEnd="@drawable/ic_arrow_drop_down_dark"
android:textSize="@dimen/regularTextSize" android:padding="16dp"
android:padding="16dp" android:text="@string/every_day"
android:text="@string/every_day" android:textColor="?attr/highContrastTextColor"
android:textColor="?attr/highContrastTextColor" /> android:textSize="@dimen/regularTextSize" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<!-- Reminder --> <!-- Reminder -->
<FrameLayout <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-17dp" android:layout_marginTop="-17dp"
android:layout_marginBottom="-4dp" android:layout_marginBottom="-4dp"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:textSize="@dimen/smallTextSize" android:paddingStart="8dp"
android:paddingStart="8dp" android:paddingEnd="8dp"
android:paddingEnd="8dp" android:text="@string/reminder"
android:text="@string/reminder" /> android:textSize="@dimen/smallTextSize" />
<TextView <TextView
android:layout_width="match_parent" android:id="@+id/reminderTimePicker"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:background="?attr/selectableItemBackground" android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_arrow_drop_down_dark" android:drawableEnd="@drawable/ic_arrow_drop_down_dark"
android:textSize="@dimen/regularTextSize" android:padding="16dp"
android:textColor="?attr/highContrastTextColor" android:text="@string/reminder_off"
android:padding="16dp" android:textColor="?attr/highContrastTextColor"
android:text="@string/reminder_off" /> android:textSize="@dimen/regularTextSize" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<!-- Notes --> <!-- Notes -->
<FrameLayout <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_input_group"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-17dp" android:layout_marginTop="-17dp"
android:layout_marginBottom="-4dp" android:layout_marginBottom="-4dp"
android:background="?attr/highContrastReverseTextColor" android:background="?attr/highContrastReverseTextColor"
android:textSize="@dimen/smallTextSize" android:paddingStart="8dp"
android:paddingStart="8dp" android:paddingEnd="8dp"
android:paddingEnd="8dp" android:text="Notes"
android:text="Notes" /> android:textSize="@dimen/smallTextSize" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/transparent" android:background="@color/transparent"
android:textSize="@dimen/regularTextSize" android:hint="(Optional)"
android:textColor="?attr/highContrastTextColor" android:padding="16dp"
android:padding="16dp" android:textColor="?attr/highContrastTextColor"
android:hint="(Optional)" /> android:textSize="@dimen/regularTextSize" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>

@ -252,5 +252,7 @@
<string name="measurable_example">e.g. How many miles did you run today? How many pages did you read? How many calories did you eat?</string> <string name="measurable_example">e.g. How many miles did you run today? How many pages did you read? How many calories did you eat?</string>
<string name="subjective">Subjective</string> <string name="subjective">Subjective</string>
<string name="subjective_example">e.g. Are you felling happy today? Definitely, somewhat, not at all? How frequently did you snack? Very often, sometimes, never?</string> <string name="subjective_example">e.g. Are you felling happy today? Definitely, somewhat, not at all? How frequently did you snack? Very often, sometimes, never?</string>
<string name="x_times_per_week">%d times per week</string>
<string name="x_times_per_month">%d times per month</string>
</resources> </resources>
Loading…
Cancel
Save