Numpad looking good

This commit is contained in:
Phillip Hsu
2016-08-22 03:45:58 -07:00
parent 9b7ab3e644
commit c0d100d165
22 changed files with 351 additions and 13 deletions
@@ -52,6 +52,11 @@ public class MainActivity extends BaseActivity {
@Bind(R.id.fab)
FloatingActionButton mFab;
// // https://medium.com/@chrisbanes/appcompat-v23-2-age-of-the-vectors-91cbafa87c88#.141274xy8
// // This is needed to load vector drawables from 23.4.0
// static {
// AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -14,6 +14,7 @@ import butterknife.ButterKnife;
* Created by Phillip Hsu on 7/16/2016.
*/
public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment {
private static final String TAG = "BaseTimePickerDialog";
// TODO: Consider private access, and then writing package/protected API that subclasses
// can use to interface with this field.
@@ -53,9 +54,59 @@ public abstract class BaseTimePickerDialog extends BottomSheetDialogFragment {
// getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
final View view = inflater.inflate(contentLayout(), container, false);
ButterKnife.bind(this, view);
// TODO: We could move this to onCreateDialog() if we cared.
//
// onShow() is called immediately as this DialogFragment is showing, so the
// FAB's animation will barely be noticeable.
// getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
// @Override
// public void onShow(DialogInterface dialog) {
// Log.i(TAG, "onShow()");
// // Animate the FAB into view
// View v = view.findViewById(R.id.fab);
// if (v != null) {
// FloatingActionButton fab = (FloatingActionButton) v;
// fab.show();
// }
// }
// });
return view;
}
// @Override
// public void onResume() {
// super.onResume();
// final View view = getView();
// final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
// // Copy over the internal callback logic, and also implement our own
// //
// // This callback is set AFTER this Fragment has become visible, so is useless for what
// // you wanted to do (show the FAB during the settling phase).
// behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
// @Override
// public void onStateChanged(@NonNull View bottomSheet, int newState) {
// Log.i(TAG, "onStateChanged(): " + newState);
// if (newState == BottomSheetBehavior.STATE_HIDDEN) {
// dismiss();
// }
// // My logic below
// else if (newState == BottomSheetBehavior.STATE_SETTLING) {
// View fab = view.findViewById(R.id.fab);
// if (fab != null) {
// ((FloatingActionButton) fab).show();
// }
// }
// }
//
// @Override
// public void onSlide(@NonNull View bottomSheet, float slideOffset) {
//
// }
// });
// }
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -1,8 +1,10 @@
package com.philliphsu.clock2.editalarm;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.CallSuper;
import android.support.annotation.LayoutRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayout;
import android.util.AttributeSet;
import android.view.View;
@@ -23,6 +25,10 @@ import butterknife.OnClick;
* Unlike Numpad, this class only manages the logic for number button clicks
* and not the backspace button. However, we do provide an API for removing
* digits from the input.
*
* TODO: Is NumpadTimePicker the only subclass? If so, why do we need this
* superclass? If we move the contents of this class to NumpadTimePicker,
* the implementation of setTheme() would make more sense.
*/
public abstract class GridLayoutNumpad extends GridLayout {
// TODO: change to private?
@@ -33,6 +39,8 @@ public abstract class GridLayoutNumpad extends GridLayout {
private int mCount = 0;
private OnInputChangeListener mOnInputChangeListener;
ColorStateList mColors;
@Bind({ R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine })
TextView[] mButtons;
@@ -64,6 +72,24 @@ public abstract class GridLayoutNumpad extends GridLayout {
init();
}
void setTheme(Context context, boolean themeDark) {
// Since the Dialog class already set the background color of its entire view tree,
// our background is already colored. Why did we set it in the Dialog class? Because
// we use margins around the numpad, and if we had instead set the background on
// this numpad here, the margins will not be colored. Why not use padding instead
// of margins? It turns out we tried that--replacing each margin attribute
// with the padding counterpart--but we lost the pre-21 FAB inherent bottom margin.
// The buttons are actually of type Button, but we kept references
// to them as TextViews... which is fine since TextView is the superclass
// of Button.
mColors = ContextCompat.getColorStateList(context,
themeDark? R.color.numeric_keypad_button_text_dark : R.color.numeric_keypad_button_text);
for (TextView b : mButtons) {
b.setTextColor(mColors);
}
}
/**
* @return the number of digits we can input
*/
@@ -470,6 +470,13 @@ public class NumberGridTimePickerDialog extends BaseTimePickerDialog implements
// Set the color on the FAB
// http://stackoverflow.com/a/32031019/5055032
// *****************************************************************************************
// NOTE: IF YOU DECIDE TO MOVE THE FAB AND THE HALF DAY TOGGLES TO THE GRIDSELECTORLAYOUT
// CLASS, YOU SHOULD CHANGE THE CONTEXT PASSED TO GridSelectorLayout#setTheme() FROM THE
// APPLICATION CONTEXT TO THE LOCAL CONTEXT. OTHERWISE, IT WOULD NOT BE ABLE TO RETRIEVE
// THE CORRECT ACCENT COLOR. WE ALREADY FACED THIS ISSUE WITH THE NUMPADTIMEPICKERDIALOG.
// DO A CTRL+F FOR mTimePicker.setTheme FOR THE CODE IN DISCUSSION.
// *****************************************************************************************
// Color in normal state
mDoneButton.setBackgroundTintList(ColorStateList.valueOf(accentColor));
// Color in pressed state. A ripple expands outwards from the point of contact throughout
@@ -1,8 +1,12 @@
package com.philliphsu.clock2.editalarm;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.widget.Button;
@@ -10,6 +14,8 @@ import android.widget.ImageButton;
import android.widget.TextView;
import com.philliphsu.clock2.R;
import com.philliphsu.clock2.aospdatetimepicker.Utils;
import com.philliphsu.clock2.util.ConversionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -55,6 +61,11 @@ public class NumpadTimePicker extends GridLayoutNumpad {
@Bind(R.id.fab) FloatingActionButton mFab;
@Bind(R.id.backspace) ImageButton mBackspace;
private boolean mThemeDark;
private int mAccentColor;
private int mFabDisabledColorDark;
private int mFabDisabledColorLight;
/**
* Provides additional APIs to configure clients' display output.
*/
@@ -75,7 +86,53 @@ public class NumpadTimePicker extends GridLayoutNumpad {
super(context, attrs);
init();
}
@Override
void setTheme(Context context, boolean themeDark) {
super.setTheme(context, themeDark);
mThemeDark = themeDark;
// So, we kept the 0-9 buttons as TextViews, but here we kept
// the alt buttons as actual Buttons...
for (Button b : mAltButtons) {
b.setTextColor(mColors);
}
// Get a mutable instance of the drawable, so we only affect this instance.
// This is especially useful when you need to modify properties of drawables loaded from
// resources. By default, all drawables instances loaded from the same resource share a
// common state; if you modify the state of one instance, all the other instances will
// receive the same modification.
// TODO: What is the VectorDrawable counterpart for this process?
Drawable backspaceDrawable = mBackspace.getDrawable().mutate();
// Wrap drawable so that it may be used for tinting across the different
// API levels, via the tinting methods in this class.
backspaceDrawable = DrawableCompat.wrap(backspaceDrawable);
// Prepare the tints
ColorStateList colorBackspace = ContextCompat.getColorStateList(context,
themeDark? R.color.icon_color_dark : R.color.icon_color);
DrawableCompat.setTintList(backspaceDrawable, colorBackspace);
mBackspace.setImageDrawable(backspaceDrawable);
// TODO: What is the VectorDrawable counterpart for this process?
Drawable iconDrawable = mFab.getDrawable().mutate();
iconDrawable = DrawableCompat.wrap(iconDrawable);
ColorStateList colorIcon = ContextCompat.getColorStateList(context,
themeDark? R.color.icon_color_dark : R.color.fab_icon_color);
DrawableCompat.setTintList(iconDrawable, colorIcon);
mFab.setImageDrawable(iconDrawable);
// this.getContext() ==> default teal accent color
// application context ==> white
// The Context that was passed in is NumpadTimePickerDialog.getContext() which
// is probably the host Activity. I have no idea what this.getContext() returns,
// but its probably some internal type that isn't tied to any of our application
// components.
mAccentColor = Utils.getThemeAccentColor(context);
// Make sure the dark theme disabled color shows up initially
updateFabState();
}
@Override
public int capacity() {
return MAX_DIGITS;
@@ -255,6 +312,9 @@ public class NumpadTimePicker extends GridLayoutNumpad {
}
private void init() {
mFabDisabledColorDark = ContextCompat.getColor(getContext(), R.color.fab_disabled_dark);
mFabDisabledColorLight = ContextCompat.getColor(getContext(), R.color.fab_disabled_light);
// TODO: We should have the user pass in is24HourMode when they create an instance of the dialog.
if (DateFormat.is24HourFormat(getContext())) {
mAltButtons[0].setText(R.string.left_alt_24hr);
mAltButtons[1].setText(R.string.right_alt_24hr);
@@ -383,6 +443,19 @@ public class NumpadTimePicker extends GridLayoutNumpad {
private void updateFabState() {
mFab.setEnabled(checkTimeValid());
// Workaround for mFab.setBackgroundTintList() because I don't know how to reference the
// correct accent color in XML. Also because I don't want to programmatically create a
// ColorStateList.
int color;
// TODO: Animate elevation property "compatElevation"
if (mFab.isEnabled()) {
color = mAccentColor;
mFab.setCompatElevation(ConversionUtils.dpToPx(getContext(), 6));
} else {
color = mThemeDark? mFabDisabledColorDark : mFabDisabledColorLight;
mFab.setCompatElevation(0);
}
mFab.setBackgroundTintList(ColorStateList.valueOf(color));
}
private void updateBackspaceState() {
@@ -1,12 +1,14 @@
package com.philliphsu.clock2.editalarm;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.philliphsu.clock2.R;
import com.philliphsu.clock2.aospdatetimepicker.Utils;
import butterknife.Bind;
import butterknife.OnClick;
@@ -36,6 +38,7 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog
* depends on the dialog to save its state.
*/
private int[] mInputtedDigits;
private boolean mThemeDark;
// Don't need to keep a reference to the dismiss ImageButton
@Bind(R.id.input_time) TextView mInputField;
@@ -54,7 +57,9 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog
// TODO: is24HourMode param
public static NumpadTimePickerDialog newInstance(OnTimeSetListener callback) {
NumpadTimePickerDialog ret = new NumpadTimePickerDialog();
// TODO: Do these in initialize()
ret.setOnTimeSetListener(callback);
ret.mThemeDark = false;
return ret;
}
@@ -67,6 +72,17 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog
mIs24HourMode = is24HourMode;
}
/**
* Set a dark or light theme. NOTE: this will only take effect for the next onCreateView.
*/
public void setThemeDark(boolean dark) {
mThemeDark = dark;
}
public boolean isThemeDark() {
return mThemeDark;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -84,8 +100,24 @@ public class NumpadTimePickerDialog extends BaseTimePickerDialog
mNumpad.insertDigits(mInputtedDigits); // TOneverDO: before mNumpad.setOnInputChangeListener(this);
// Show the cursor immediately
// mInputField.requestFocus();
// TODO: Disabled color
//updateInputText(""); // Primarily to disable 'OK'
// Prepare colors
int accentColor = Utils.getThemeAccentColor(getContext());
int lightGray = ContextCompat.getColor(getContext(), R.color.light_gray);
int darkGray = ContextCompat.getColor(getContext(), R.color.dark_gray);
int white = ContextCompat.getColor(getContext(), android.R.color.white);
// Set background color of entire view
view.setBackgroundColor(mThemeDark? darkGray : white);
TextView inputTime = (TextView) view.findViewById(R.id.input_time);
inputTime.setBackgroundColor(mThemeDark? lightGray : accentColor);
inputTime.setTextColor(ContextCompat.getColor(getContext(), android.R.color.white));
mNumpad.setTheme(getContext()/*DO NOT GIVE THE APPLICATION CONTEXT, OR ELSE THE NUMPAD
CAN'T GET THE CORRECT ACCENT COLOR*/, mThemeDark);
return view;
}