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

View File

@ -10,6 +10,9 @@ android {
targetSdkVersion 23
versionCode 1
versionName "1.0"
// Disabled for now because we're not ready to
// completely port over to vector drawables
// vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {

View File

@ -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) {

View File

@ -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();

View File

@ -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
*/

View File

@ -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

View File

@ -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.
*/
@ -76,6 +87,52 @@ public class NumpadTimePicker extends GridLayoutNumpad {
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() {

View File

@ -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;
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--A variation of icon_color that correctly gives the enabled color for FABs -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/icon_color_inactive_light"/>
<item android:color="@android:color/white"/>
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/icon_color_inactive_light"/>
<item android:color="@color/icon_color_active_light"/>
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/icon_color_inactive_dark"/>
<item android:color="@color/icon_color_active_dark"/>
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/text_color_disabled_light"/>
<item android:color="@color/text_color_primary_light"/>
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/text_color_disabled_dark"/>
<item android:color="@color/text_color_primary_dark"/>
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/fab_disabled_light"/>
<item android:color="?colorAccent"/>
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/fab_disabled_dark"/>
<item android:color="?colorAccent"/>
</selector>

View File

@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:fillColor="#000000"
android:pathData="M22,3H7c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-3,12.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z"/>
</vector>

View File

@ -43,7 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:src="@drawable/ic_add_24dp"
app:srcCompat="@drawable/ic_add_24dp"
android:layout_margin="@dimen/fab_margin"
android:tint="@android:color/white"/>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto">
<Button
android:id="@+id/one"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="1"/>
<Button
android:id="@+id/two"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="2"/>
<Button
android:id="@+id/three"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="3"/>
<Button
android:id="@+id/four"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="4"/>
<Button
android:id="@+id/five"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="5"/>
<Button
android:id="@+id/six"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="6"/>
<Button
android:id="@+id/seven"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="7"/>
<Button
android:id="@+id/eight"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="8"/>
<Button
android:id="@+id/nine"
style="@style/GridLayoutNumpadButton.Inverse"
android:text="9"/>
<Button
android:id="@+id/zero"
style="@style/GridLayoutNumpadButton.Inverse"
grid:layout_column="1"
android:text="0"/>
</merge>

View File

@ -20,12 +20,13 @@
android:layout_height="wrap_content"
app:layout_gravity="center"
app:layout_column="1"
android:src="@drawable/ic_done_24dp"
app:srcCompat="@drawable/ic_done_24dp"
app:borderWidth="0dp"
android:layout_marginBottom="@dimen/numeric_keypad_fab_bottom_margin"/>
<ImageButton
android:id="@+id/backspace"
android:src="@drawable/ic_backspace_24dp"
app:srcCompat="@drawable/ic_backspace_24dp"
android:layout_height="@dimen/numeric_keypad_backspace_height"
style="@style/GridLayoutNumpadElement"
app:layout_column="2"/>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<include layout="@layout/content_grid_layout_numpad_dark"/>
<Button
android:id="@+id/leftAlt"
style="@style/GridLayoutNumpadButton.Inverse"
app:layout_column="0"/>
<Button
android:id="@+id/rightAlt"
style="@style/GridLayoutNumpadButton.Inverse"
app:layout_column="2"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_gravity="center"
app:layout_column="1"
android:src="@drawable/ic_done_24dp"
android:layout_marginBottom="@dimen/numeric_keypad_fab_bottom_margin"/>
<ImageButton
android:id="@+id/backspace"
android:src="@drawable/ic_backspace_24dp"
android:layout_height="@dimen/numeric_keypad_backspace_height"
style="@style/GridLayoutNumpadElement"
app:layout_column="2"/>
</merge>

View File

@ -13,21 +13,17 @@ doesn't work. -->
android:layout_height="@dimen/numeric_keypad_output_box_height"
android:gravity="center"
android:textSize="@dimen/text_size_display_3"
android:fontFamily="sans-serif-light"
android:fontFamily="sans-serif"
style="@style/TextAppearance.AppCompat"/>
<!--<View style="@style/FocusGrabber"
android:id="@+id/focus_grabber"/>-->
<View style="@style/Divider.Horizontal"
android:id="@+id/header_divider"
android:layout_below="@id/input_time"/>
<com.philliphsu.clock2.editalarm.NumpadTimePicker
android:id="@+id/number_grid"
android:layout_width="match_parent"
android:layout_height="@dimen/numeric_keypad_height"
android:layout_below="@id/header_divider"
android:layout_below="@id/input_time"
android:layout_marginTop="@dimen/bottom_sheet_vertical_space"
android:layout_marginStart="@dimen/bottom_sheet_edge_margin"
android:layout_marginEnd="@dimen/bottom_sheet_edge_margin"/>

View File

@ -51,4 +51,17 @@
<color name="text_color_primary_light">#de000000</color> <!--87% black-->
<color name="text_color_secondary_dark">#b3ffffff</color> <!--70% white-->
<color name="text_color_secondary_light">#8a000000</color> <!--54% black-->
<color name="text_color_disabled_dark">#80ffffff</color> <!--50% white TODO: same as unselected_color-->
<color name="text_color_disabled_light">#61000000</color> <!--38% black-->
<!-- Derived from Raised Button section of the guidelines. Make sure to set FAB elevation to 0dp! -->
<color name="fab_disabled_dark">#1fffffff</color> <!--12% white-->
<color name="fab_disabled_light">#1f000000</color> <!--12% black-->
<color name="icon_color_active_dark">#ffffffff</color> <!--100% white-->
<color name="icon_color_active_light">#8a000000</color> <!--54% black-->
<!-- TODO: This value is from the Icons page. The Colors page lists 50% white for "Icons and other elements". -->
<color name="icon_color_inactive_dark">#4dffffff</color> <!--30% white-->
<!-- TODO: This value is from the Icons page. The Colors page lists 38% black for "Icons and other elements". -->
<color name="icon_color_inactive_light">#42000000</color> <!--26% black-->
</resources>

View File

@ -45,14 +45,20 @@
<style name="GridLayoutNumpadButton" parent="GridLayoutNumpadElement">
<item name="android:textSize">@dimen/text_size_display_1</item>
<item name="android:fontFamily">sans-serif-light</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="GridLayoutNumpadButton.Inverse">
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
</style>
<style name="NumberGridButton" parent="GridLayoutNumpadButton">
<!-- TODO: Do we need this anymore? -->
<!-- This should give us 87% black. By default, TextView has a grayish text color.
The reason GridLayoutNumpadButton style used in the NumpadTimerPickerDialog is 87% black
already is because the numpad's buttons are actually of type Button. For whatever reason,
I decided to make the buttons for the NumbersGrid of type TextView... -->
<item name="android:textColor">?android:attr/textColorPrimary</item>
<!--<item name="android:textColor">?android:attr/textColorPrimary</item>-->
</style>
<style name="NumberGridButton.Hour">
<item name="android:layout_height">@dimen/number_grid_hour_cell_height</item>