Created dialog for numpad time picker
This commit is contained in:
parent
6339667dea
commit
c768559acc
@ -28,9 +28,10 @@ dependencies {
|
|||||||
testCompile 'org.robolectric:robolectric:3.0' // TODO: delete, not in use
|
testCompile 'org.robolectric:robolectric:3.0' // TODO: delete, not in use
|
||||||
provided 'com.google.auto.value:auto-value:1.2'
|
provided 'com.google.auto.value:auto-value:1.2'
|
||||||
apt 'com.google.auto.value:auto-value:1.2'
|
apt 'com.google.auto.value:auto-value:1.2'
|
||||||
compile 'com.android.support:appcompat-v7:23.2.1'
|
compile 'com.android.support:appcompat-v7:23.4.0'
|
||||||
compile 'com.android.support:design:23.2.1'
|
compile 'com.android.support:design:23.4.0'
|
||||||
compile 'com.android.support:support-v4:23.2.1'
|
compile 'com.android.support:support-v4:23.4.0'
|
||||||
compile 'com.android.support:recyclerview-v7:23.2.1'
|
compile 'com.android.support:recyclerview-v7:23.4.0'
|
||||||
|
compile 'com.android.support:gridlayout-v7:23.4.0'
|
||||||
compile 'com.jakewharton:butterknife:7.0.1'
|
compile 'com.jakewharton:butterknife:7.0.1'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -15,7 +17,8 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.philliphsu.clock2.alarms.AlarmsFragment;
|
import com.philliphsu.clock2.alarms.AlarmsFragment;
|
||||||
import com.philliphsu.clock2.editalarm.EditAlarmActivity;
|
import com.philliphsu.clock2.editalarm.NumpadTimePickerDialog;
|
||||||
|
import com.philliphsu.clock2.editalarm.OnTimeSetListener;
|
||||||
import com.philliphsu.clock2.settings.SettingsActivity;
|
import com.philliphsu.clock2.settings.SettingsActivity;
|
||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
@ -51,9 +54,18 @@ public class MainActivity extends BaseActivity {
|
|||||||
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
|
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
|
||||||
tabLayout.setupWithViewPager(mViewPager);
|
tabLayout.setupWithViewPager(mViewPager);
|
||||||
|
|
||||||
|
// TODO: @OnCLick instead.
|
||||||
mFab.setOnClickListener(new View.OnClickListener() {
|
mFab.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
NumpadTimePickerDialog tpd = NumpadTimePickerDialog.newInstance(new OnTimeSetListener() {
|
||||||
|
@Override
|
||||||
|
public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) {
|
||||||
|
Log.i(TAG, "Time set: " + String.format("%02d:%02d", hourOfDay, minute));
|
||||||
|
}
|
||||||
|
}, 0, 0, DateFormat.is24HourFormat(MainActivity.this));
|
||||||
|
tpd.show(getFragmentManager(), "tag");
|
||||||
|
/*
|
||||||
Intent intent = new Intent(MainActivity.this, EditAlarmActivity.class);
|
Intent intent = new Intent(MainActivity.this, EditAlarmActivity.class);
|
||||||
// Call Fragment#startActivityForResult() instead of Activity#startActivityForResult()
|
// Call Fragment#startActivityForResult() instead of Activity#startActivityForResult()
|
||||||
// because we want the result to be handled in the Fragment, not in this Activity.
|
// because we want the result to be handled in the Fragment, not in this Activity.
|
||||||
@ -61,6 +73,7 @@ public class MainActivity extends BaseActivity {
|
|||||||
// Fragment's onActivityResult() will NOT be called.
|
// Fragment's onActivityResult() will NOT be called.
|
||||||
mSectionsPagerAdapter.getCurrentFragment()
|
mSectionsPagerAdapter.getCurrentFragment()
|
||||||
.startActivityForResult(intent, AlarmsFragment.REQUEST_CREATE_ALARM);
|
.startActivityForResult(intent, AlarmsFragment.REQUEST_CREATE_ALARM);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@ -62,7 +63,8 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
|||||||
EditAlarmContract.View, // TODO: Remove @Override from the methods
|
EditAlarmContract.View, // TODO: Remove @Override from the methods
|
||||||
AlarmUtilsHelper,
|
AlarmUtilsHelper,
|
||||||
SharedPreferencesHelper,
|
SharedPreferencesHelper,
|
||||||
LoaderManager.LoaderCallbacks<Alarm> {
|
LoaderManager.LoaderCallbacks<Alarm>,
|
||||||
|
OnTimeSetListener {
|
||||||
private static final String TAG = "EditAlarmActivity";
|
private static final String TAG = "EditAlarmActivity";
|
||||||
public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID";
|
public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID";
|
||||||
public static final String EXTRA_MODIFIED_ALARM = "com.philliphsu.clock2.editalarm.extra.MODIFIED_ALARM";
|
public static final String EXTRA_MODIFIED_ALARM = "com.philliphsu.clock2.editalarm.extra.MODIFIED_ALARM";
|
||||||
@ -88,6 +90,11 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
|||||||
@Bind(R.id.vibrate) CheckBox mVibrate;
|
@Bind(R.id.vibrate) CheckBox mVibrate;
|
||||||
@Bind(R.id.numpad) AlarmNumpad mNumpad;
|
@Bind(R.id.numpad) AlarmNumpad mNumpad;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) {
|
||||||
|
Log.i(TAG, "Time set: " + String.format("%02d:%02d", hourOfDay, minute));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|||||||
@ -0,0 +1,222 @@
|
|||||||
|
package com.philliphsu.clock2.editalarm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.CallSuper;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.v7.widget.GridLayout;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.R;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/12/2016.
|
||||||
|
*
|
||||||
|
* Successor to the Numpad class that was based on TableLayout.
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public abstract class GridLayoutNumpad extends GridLayout implements View.OnClickListener {
|
||||||
|
// TODO: change to private?
|
||||||
|
protected static final int UNMODIFIED = -1;
|
||||||
|
private static final int COLUMNS = 3;
|
||||||
|
|
||||||
|
private int[] mInput;
|
||||||
|
private int mCount = 0;
|
||||||
|
private OnInputChangeListener mOnInputChangeListener;
|
||||||
|
|
||||||
|
@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 })
|
||||||
|
Button[] mButtons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs clients how to output the digits inputted into this numpad.
|
||||||
|
*/
|
||||||
|
public interface OnInputChangeListener {
|
||||||
|
/**
|
||||||
|
* @param newStr the new value of the input formatted as a
|
||||||
|
* String after a digit insertion
|
||||||
|
*/
|
||||||
|
void onDigitInserted(String newStr);
|
||||||
|
/**
|
||||||
|
* @param newStr the new value of the input formatted as a
|
||||||
|
* String after a digit deletion
|
||||||
|
*/
|
||||||
|
void onDigitDeleted(String newStr);
|
||||||
|
void onDigitsCleared();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GridLayoutNumpad(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GridLayoutNumpad(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of digits we can input
|
||||||
|
*/
|
||||||
|
protected abstract int capacity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the layout resource that defines the children for this numpad
|
||||||
|
*/
|
||||||
|
@LayoutRes
|
||||||
|
protected abstract int contentLayout();
|
||||||
|
|
||||||
|
public final void setOnInputChangeListener(OnInputChangeListener onInputChangeListener) {
|
||||||
|
mOnInputChangeListener = onInputChangeListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void enable(int lowerLimitInclusive, int upperLimitExclusive) {
|
||||||
|
if (lowerLimitInclusive < 0 || upperLimitExclusive > mButtons.length)
|
||||||
|
throw new IndexOutOfBoundsException("Upper limit out of range");
|
||||||
|
|
||||||
|
for (int i = 0; i < mButtons.length; i++)
|
||||||
|
mButtons[i].setEnabled(i >= lowerLimitInclusive && i < upperLimitExclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int valueAt(int index) {
|
||||||
|
return mInput[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of digits inputted
|
||||||
|
*/
|
||||||
|
protected final int count() {
|
||||||
|
return mCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the integer represented by the inputted digits
|
||||||
|
*/
|
||||||
|
protected final int getInput() {
|
||||||
|
return Integer.parseInt(getInputString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getInputString() {
|
||||||
|
String currentInput = "";
|
||||||
|
for (int i : mInput)
|
||||||
|
if (i != UNMODIFIED)
|
||||||
|
currentInput += i;
|
||||||
|
return currentInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
/*
|
||||||
|
if (mCount - 1 >= 0) {
|
||||||
|
mInput[--mCount] = UNMODIFIED;
|
||||||
|
}
|
||||||
|
onDigitDeleted(getInputString());
|
||||||
|
*/
|
||||||
|
delete(mCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Why do we need this?
|
||||||
|
public void delete(int at) {
|
||||||
|
if (at - 1 >= 0) {
|
||||||
|
mInput[at - 1] = UNMODIFIED;
|
||||||
|
mCount--;
|
||||||
|
onDigitDeleted(getInputString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void clear() {
|
||||||
|
Arrays.fill(mInput, UNMODIFIED);
|
||||||
|
mCount = 0;
|
||||||
|
onDigitsCleared();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the provided String to the assigned
|
||||||
|
* {@link OnInputChangeListener OnInputChangeListener}
|
||||||
|
* after a digit insertion. By default, the String
|
||||||
|
* forwarded is just the String value of the inserted digit.
|
||||||
|
* @see #onClick(View)
|
||||||
|
* @param newDigit the formatted String that should be displayed
|
||||||
|
*/
|
||||||
|
@CallSuper
|
||||||
|
protected void onDigitInserted(String newDigit) {
|
||||||
|
if (mOnInputChangeListener != null) {
|
||||||
|
mOnInputChangeListener.onDigitInserted(newDigit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the provided String to the assigned
|
||||||
|
* {@link OnInputChangeListener OnInputChangeListener}
|
||||||
|
* after a digit deletion. By default, the String
|
||||||
|
* forwarded is {@link #getInputString()}.
|
||||||
|
* @param newStr the formatted String that should be displayed
|
||||||
|
*/
|
||||||
|
@CallSuper
|
||||||
|
protected void onDigitDeleted(String newStr) {
|
||||||
|
if (mOnInputChangeListener != null) {
|
||||||
|
mOnInputChangeListener.onDigitDeleted(newStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards a {@code onDigitsCleared()} event to the assigned
|
||||||
|
* {@link OnInputChangeListener OnInputChangeListener}.
|
||||||
|
*/
|
||||||
|
@CallSuper
|
||||||
|
protected void onDigitsCleared() {
|
||||||
|
if (mOnInputChangeListener != null) {
|
||||||
|
mOnInputChangeListener.onDigitsCleared();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts as many of the digits in the given sequence
|
||||||
|
* into the input as possible. At the end, if any digits
|
||||||
|
* were inserted, this calls {@link #onDigitInserted(String)}
|
||||||
|
* with the String value of those digits.
|
||||||
|
*/
|
||||||
|
protected final void insertDigits(int... digits) {
|
||||||
|
String newDigits = "";
|
||||||
|
for (int d : digits) {
|
||||||
|
if (mCount == mInput.length)
|
||||||
|
break;
|
||||||
|
mInput[mCount++] = d;
|
||||||
|
newDigits += d;
|
||||||
|
}
|
||||||
|
if (!newDigits.isEmpty()) {
|
||||||
|
// By only calling this once after making
|
||||||
|
// the insertions, we skip all of the
|
||||||
|
// intermediate callbacks.
|
||||||
|
onDigitInserted(newDigits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Why not @OnClick instead?
|
||||||
|
@Override
|
||||||
|
public final void onClick(View v) {
|
||||||
|
if (mCount < mInput.length) {
|
||||||
|
String textNum = ((Button) v).getText().toString();
|
||||||
|
insertDigits(Integer.parseInt(textNum));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
setAlignmentMode(ALIGN_BOUNDS);
|
||||||
|
setColumnCount(COLUMNS);
|
||||||
|
View.inflate(getContext(), contentLayout(), this);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
for (Button b : mButtons)
|
||||||
|
b.setOnClickListener(this);
|
||||||
|
// If capacity() < 0, we let the system throw the exception.
|
||||||
|
mInput = new int[capacity()];
|
||||||
|
Arrays.fill(mInput, UNMODIFIED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,416 @@
|
|||||||
|
package com.philliphsu.clock2.editalarm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.IntDef;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.R;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.text.DateFormatSymbols;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/12/2016.
|
||||||
|
*/
|
||||||
|
public class NumpadTimePicker extends GridLayoutNumpad {
|
||||||
|
// Time can be represented with maximum of 4 digits
|
||||||
|
private static final int MAX_DIGITS = 4;
|
||||||
|
|
||||||
|
// Formatted time string has a maximum of 8 characters
|
||||||
|
// in the 12-hour clock, e.g 12:59 AM. Although the 24-hour
|
||||||
|
// clock should be capped at 5 characters, the difference
|
||||||
|
// is not significant enough to deal with the separate cases.
|
||||||
|
private static final int MAX_CHARS = 8;
|
||||||
|
|
||||||
|
// Constant for converting text digits to numeric digits in base-10.
|
||||||
|
private static final int BASE_10 = 10;
|
||||||
|
|
||||||
|
// AmPmStates
|
||||||
|
private static final int UNSPECIFIED = -1;
|
||||||
|
private static final int AM = 0;
|
||||||
|
private static final int PM = 1;
|
||||||
|
private static final int HRS_24 = 2;
|
||||||
|
|
||||||
|
@IntDef({ UNSPECIFIED, AM, PM, HRS_24 }) // Specifies the accepted constants
|
||||||
|
@Retention(RetentionPolicy.SOURCE) // Usages do not need to be recorded in .class files
|
||||||
|
private @interface AmPmState {}
|
||||||
|
|
||||||
|
@AmPmState
|
||||||
|
private int mAmPmState = UNSPECIFIED;
|
||||||
|
private final StringBuilder mFormattedInput = new StringBuilder(MAX_CHARS);
|
||||||
|
|
||||||
|
@Bind({ R.id.leftAlt, R.id.rightAlt })
|
||||||
|
Button[] mAltButtons;
|
||||||
|
|
||||||
|
public NumpadTimePicker(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumpadTimePicker(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int capacity() {
|
||||||
|
return MAX_DIGITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int contentLayout() {
|
||||||
|
return R.layout.content_numpad_time_picker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDigitInserted(String newDigit) {
|
||||||
|
// Append the new digit(s) to the formatter
|
||||||
|
updateFormattedInputOnDigitInserted(newDigit);
|
||||||
|
super.onDigitInserted(mFormattedInput.toString());
|
||||||
|
updateNumpadStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDigitDeleted(String newStr) {
|
||||||
|
updateFormattedInputOnDigitDeleted();
|
||||||
|
super.onDigitDeleted(mFormattedInput.toString());
|
||||||
|
updateNumpadStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDigitsCleared() {
|
||||||
|
mFormattedInput.delete(0, mFormattedInput.length());
|
||||||
|
updateNumpadStates();
|
||||||
|
mAmPmState = UNSPECIFIED;
|
||||||
|
super.onDigitsCleared();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() {
|
||||||
|
int len = mFormattedInput.length();
|
||||||
|
if (!is24HourFormat() && mAmPmState != UNSPECIFIED) {
|
||||||
|
mAmPmState = UNSPECIFIED;
|
||||||
|
// Delete starting from index of space to end
|
||||||
|
mFormattedInput.delete(mFormattedInput.indexOf(" "), len);
|
||||||
|
// No digit was actually deleted, but we have to notify the
|
||||||
|
// listener to update its output.
|
||||||
|
/*TOneverDO: remove super*/super.onDigitDeleted(
|
||||||
|
mFormattedInput.toString());
|
||||||
|
updateNumpadStates();
|
||||||
|
} else {
|
||||||
|
super.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the hour of day (0-23) regardless of clock system */
|
||||||
|
public int getHours() {
|
||||||
|
if (!checkTimeValid())
|
||||||
|
throw new IllegalStateException("Cannot call getHours() until legal time inputted");
|
||||||
|
int hours = count() < 4 ? valueAt(0) : valueAt(0) * 10 + valueAt(1);
|
||||||
|
if (hours == 12) {
|
||||||
|
switch (mAmPmState) {
|
||||||
|
case AM:
|
||||||
|
return 0;
|
||||||
|
case PM:
|
||||||
|
case HRS_24:
|
||||||
|
return 12;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AM/PM clock needs value offset
|
||||||
|
return hours + (mAmPmState == PM ? 12 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinutes() {
|
||||||
|
if (!checkTimeValid())
|
||||||
|
throw new IllegalStateException("Cannot call getMinutes() until legal time inputted");
|
||||||
|
return count() < 4 ? valueAt(1) * 10 + valueAt(2) : valueAt(2) * 10 + valueAt(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the input stored so far qualifies as a valid time.
|
||||||
|
* For this to return {@code true}, the hours, minutes AND AM/PM
|
||||||
|
* state must be set.
|
||||||
|
*/
|
||||||
|
public boolean checkTimeValid() {
|
||||||
|
if (mAmPmState == UNSPECIFIED || mAmPmState == HRS_24 && count() < 3)
|
||||||
|
return false;
|
||||||
|
// AM or PM can only be set if the time was already valid previously, so we don't need
|
||||||
|
// to check for them.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(int hours, int minutes) {
|
||||||
|
if (hours < 0 || hours > 23)
|
||||||
|
throw new IllegalArgumentException("Illegal hours: " + hours);
|
||||||
|
if (minutes < 0 || minutes > 59)
|
||||||
|
throw new IllegalArgumentException("Illegal minutes: " + minutes);
|
||||||
|
|
||||||
|
// Internal representation of the time has been checked for legality.
|
||||||
|
// Now we need to format it depending on the user's clock system.
|
||||||
|
// If 12-hour clock, can't set mAmPmState yet or else this interferes
|
||||||
|
// with the button state update mechanism. Instead, cache the state
|
||||||
|
// the hour would resolve to in a local variable and set it after
|
||||||
|
// all digits are inputted.
|
||||||
|
int amPmState;
|
||||||
|
if (!is24HourFormat()) {
|
||||||
|
// Convert 24-hour times into 12-hour compatible times.
|
||||||
|
if (hours == 0) {
|
||||||
|
hours = 12;
|
||||||
|
amPmState = AM;
|
||||||
|
} else if (hours == 12) {
|
||||||
|
amPmState = PM;
|
||||||
|
} else if (hours > 12) {
|
||||||
|
hours -= 12;
|
||||||
|
amPmState = PM;
|
||||||
|
} else {
|
||||||
|
amPmState = AM;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
amPmState = HRS_24;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the hour and minutes into text form, so that
|
||||||
|
// we can read each digit individually.
|
||||||
|
// Only if on 24-hour clock, zero-pad single digit hours.
|
||||||
|
// Zero cannot be the first digit of any time in the 12-hour clock.
|
||||||
|
String textDigits = is24HourFormat()
|
||||||
|
? String.format("%02d", hours)
|
||||||
|
: String.valueOf(hours);
|
||||||
|
textDigits += String.format("%02d", minutes);
|
||||||
|
|
||||||
|
int[] digits = new int[textDigits.length()];
|
||||||
|
for (int i = 0; i < textDigits.length(); i++) {
|
||||||
|
digits[i] = Character.digit(textDigits.charAt(i), BASE_10);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertDigits(digits);
|
||||||
|
|
||||||
|
mAmPmState = amPmState;
|
||||||
|
if (mAmPmState != HRS_24) {
|
||||||
|
onAltButtonClick(mAmPmState == AM ? mAltButtons[0] : mAltButtons[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTime() {
|
||||||
|
return mFormattedInput.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
if (is24HourFormat()) {
|
||||||
|
mAltButtons[0].setText(R.string.left_alt_24hr);
|
||||||
|
mAltButtons[1].setText(R.string.right_alt_24hr);
|
||||||
|
} else {
|
||||||
|
String[] amPmTexts = new DateFormatSymbols().getAmPmStrings();
|
||||||
|
mAltButtons[0].setText(amPmTexts[Calendar.AM]);
|
||||||
|
mAltButtons[1].setText(amPmTexts[Calendar.PM]);
|
||||||
|
}
|
||||||
|
updateNumpadStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick({ R.id.leftAlt, R.id.rightAlt })
|
||||||
|
void onClick(Button altBtn) {
|
||||||
|
onAltButtonClick(altBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAltButtonClick(Button altBtn) {
|
||||||
|
if (mAltButtons[0] != altBtn && mAltButtons[1] != altBtn)
|
||||||
|
throw new IllegalArgumentException("Not called with one of the alt buttons");
|
||||||
|
|
||||||
|
// Manually insert special characters for 12-hour clock
|
||||||
|
if (!is24HourFormat()) {
|
||||||
|
if (count() <= 2) {
|
||||||
|
// The colon is inserted for you
|
||||||
|
insertDigits(0, 0);
|
||||||
|
}
|
||||||
|
// text is AM or PM, so include space before
|
||||||
|
mFormattedInput.append(' ').append(altBtn.getText());
|
||||||
|
mAmPmState = mAltButtons[0] == altBtn ? AM : PM;
|
||||||
|
// Digits will be shown for you on insert, but not AM/PM
|
||||||
|
/*TOneverDO: remove super*/super.onDigitInserted(
|
||||||
|
mFormattedInput.toString());
|
||||||
|
} else {
|
||||||
|
CharSequence text = altBtn.getText();
|
||||||
|
int[] digits = new int[text.length() - 1];
|
||||||
|
// charAt(0) is the colon, so skip i = 0.
|
||||||
|
// We are only interested in storing the digits.
|
||||||
|
for (int i = 1; i < text.length(); i++) {
|
||||||
|
digits[i] = Character.digit(text.charAt(i), BASE_10);
|
||||||
|
}
|
||||||
|
// Colon is added for you
|
||||||
|
insertDigits(digits);
|
||||||
|
mAmPmState = HRS_24;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNumpadStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean is24HourFormat() {
|
||||||
|
return DateFormat.is24HourFormat(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFormattedInputOnDigitInserted(String newDigits) {
|
||||||
|
mFormattedInput.append(newDigits);
|
||||||
|
// Add colon if necessary, depending on how many digits entered so far
|
||||||
|
if (count() == 3) {
|
||||||
|
// Insert a colon
|
||||||
|
int digits = getInput();
|
||||||
|
if (digits >= 60 && digits < 100 || digits >= 160 && digits < 200) {
|
||||||
|
// From 060-099 (really only to 095, but might as well go up to 100)
|
||||||
|
// From 160-199 (really only to 195, but might as well go up to 200),
|
||||||
|
// time does not exist if colon goes at pos. 1
|
||||||
|
mFormattedInput.insert(2, ':');
|
||||||
|
// These times only apply to the 24-hour clock, and if we're here,
|
||||||
|
// the time is not legal yet. So we can't set mAmPmState here for
|
||||||
|
// either clock.
|
||||||
|
// The 12-hour clock can only have mAmPmState set when AM/PM are clicked.
|
||||||
|
} else {
|
||||||
|
// A valid time exists if colon is at pos. 1
|
||||||
|
mFormattedInput.insert(1, ':');
|
||||||
|
// We can set mAmPmState here (and not in the above case) because
|
||||||
|
// the time here is legal in 24-hour clock
|
||||||
|
if (is24HourFormat()) {
|
||||||
|
mAmPmState = HRS_24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (count() == MAX_DIGITS) {
|
||||||
|
int colonAt = mFormattedInput.indexOf(":");
|
||||||
|
// Since we now batch updating the formatted input whenever
|
||||||
|
// digits are inserted, the colon may legitimately not be
|
||||||
|
// present in the formatted input when this is initialized.
|
||||||
|
if (colonAt != -1) {
|
||||||
|
// Colon needs to move, so remove the colon previously added
|
||||||
|
mFormattedInput.deleteCharAt(colonAt);
|
||||||
|
}
|
||||||
|
mFormattedInput.insert(2, ':');
|
||||||
|
|
||||||
|
// Time is legal in 24-hour clock
|
||||||
|
if (is24HourFormat()) {
|
||||||
|
mAmPmState = HRS_24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFormattedInputOnDigitDeleted() {
|
||||||
|
int len = mFormattedInput.length();
|
||||||
|
mFormattedInput.delete(len - 1, len);
|
||||||
|
if (count() == 3) {
|
||||||
|
// Move the colon from its 4-digit position to its 3-digit position,
|
||||||
|
// unless doing so gives an invalid time.
|
||||||
|
// e.g. 17:55 becomes 1:75, which is invalid.
|
||||||
|
// All 3-digit times in the 12-hour clock at this point should be
|
||||||
|
// valid. The limits <=155 and (>=200 && <=235) are really only
|
||||||
|
// imposed on the 24-hour clock, and were chosen because 4-digit times
|
||||||
|
// in the 24-hour clock can only go up to 15:5[0-9] or be within the range
|
||||||
|
// [20:00, 23:59] if they are to remain valid when they become three digits.
|
||||||
|
// The is24HourFormat() check is therefore unnecessary.
|
||||||
|
int value = getInput();
|
||||||
|
if (value <= 155 || value >= 200 && value <= 235) {
|
||||||
|
mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":"));
|
||||||
|
mFormattedInput.insert(1, ":");
|
||||||
|
}
|
||||||
|
} else if (count() == 2) {
|
||||||
|
// Remove the colon
|
||||||
|
mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNumpadStates() {
|
||||||
|
updateAltButtonStates();
|
||||||
|
//updateBackspaceState(); // Backspace is not part of the numpad
|
||||||
|
updateNumberKeysStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAltButtonStates() {
|
||||||
|
if (count() == 0) {
|
||||||
|
// No input, no access!
|
||||||
|
mAltButtons[0].setEnabled(false);
|
||||||
|
mAltButtons[1].setEnabled(false);
|
||||||
|
} else if (count() == 1) {
|
||||||
|
// Any of 0-9 inputted, always have access in either clock.
|
||||||
|
mAltButtons[0].setEnabled(true);
|
||||||
|
mAltButtons[1].setEnabled(true);
|
||||||
|
} else if (count() == 2) {
|
||||||
|
// Any 2 digits that make a valid hour for either clock are eligible for access
|
||||||
|
int time = getInput();
|
||||||
|
boolean validTwoDigitHour = is24HourFormat() ? time <= 23 : time >= 10 && time <= 12;
|
||||||
|
mAltButtons[0].setEnabled(validTwoDigitHour);
|
||||||
|
mAltButtons[1].setEnabled(validTwoDigitHour);
|
||||||
|
} else if (count() == 3) {
|
||||||
|
if (is24HourFormat()) {
|
||||||
|
// For the 24-hour clock, no access at all because
|
||||||
|
// two more digits (00 or 30) cannot be added to 3 digits.
|
||||||
|
mAltButtons[0].setEnabled(false);
|
||||||
|
mAltButtons[1].setEnabled(false);
|
||||||
|
} else {
|
||||||
|
// True for any 3 digits, if AM/PM not already entered
|
||||||
|
boolean enabled = mAmPmState == UNSPECIFIED;
|
||||||
|
mAltButtons[0].setEnabled(enabled);
|
||||||
|
mAltButtons[1].setEnabled(enabled);
|
||||||
|
}
|
||||||
|
} else if (count() == MAX_DIGITS) {
|
||||||
|
// If all 4 digits are filled in, the 24-hour clock has absolutely
|
||||||
|
// no need for the alt buttons. However, The 12-hour clock has
|
||||||
|
// complete need of them, if not already used.
|
||||||
|
boolean enabled = !is24HourFormat() && mAmPmState == UNSPECIFIED;
|
||||||
|
mAltButtons[0].setEnabled(enabled);
|
||||||
|
mAltButtons[1].setEnabled(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNumberKeysStates() {
|
||||||
|
int cap = 10; // number of buttons
|
||||||
|
boolean is24hours = is24HourFormat();
|
||||||
|
|
||||||
|
if (count() == 0) {
|
||||||
|
enable(is24hours ? 0 : 1, cap);
|
||||||
|
return;
|
||||||
|
} else if (count() == MAX_DIGITS) {
|
||||||
|
enable(0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int time = getInput();
|
||||||
|
if (is24hours) {
|
||||||
|
if (count() == 1) {
|
||||||
|
enable(0, time < 2 ? cap : 6);
|
||||||
|
} else if (count() == 2) {
|
||||||
|
enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 6);
|
||||||
|
} else if (count() == 3) {
|
||||||
|
if (time >= 236) {
|
||||||
|
enable(0, 0);
|
||||||
|
} else {
|
||||||
|
enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (count() == 1) {
|
||||||
|
if (time == 0) {
|
||||||
|
throw new IllegalStateException("12-hr format, zeroth digit = 0?");
|
||||||
|
} else {
|
||||||
|
enable(0, 6);
|
||||||
|
}
|
||||||
|
} else if (count() == 2 || count() == 3) {
|
||||||
|
if (time >= 126) {
|
||||||
|
enable(0, 0);
|
||||||
|
} else {
|
||||||
|
if (time >= 100 && time <= 125 && mAmPmState != UNSPECIFIED) {
|
||||||
|
// Could legally input fourth digit, if not for the am/pm state already set
|
||||||
|
enable(0, 0);
|
||||||
|
} else {
|
||||||
|
enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
package com.philliphsu.clock2.editalarm;
|
||||||
|
|
||||||
|
import android.app.DialogFragment;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.R;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
|
import butterknife.OnLongClick;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/12/2016.
|
||||||
|
*
|
||||||
|
* Note this extends the framework's DialogFragment, NOT the support version's!
|
||||||
|
*/
|
||||||
|
public class NumpadTimePickerDialog extends DialogFragment
|
||||||
|
implements NumpadTimePicker.OnInputChangeListener {
|
||||||
|
|
||||||
|
private static final String KEY_HOUR_OF_DAY = "hour_of_day";
|
||||||
|
private static final String KEY_MINUTE = "minute";
|
||||||
|
private static final String KEY_IS_24_HOUR_VIEW = "is_24_hour_view";
|
||||||
|
|
||||||
|
private OnTimeSetListener mCallback;
|
||||||
|
|
||||||
|
private int mInitialHourOfDay;
|
||||||
|
private int mInitialMinute;
|
||||||
|
private boolean mIs24HourMode;
|
||||||
|
|
||||||
|
@Bind(R.id.backspace) ImageButton mBackspace;
|
||||||
|
@Bind(R.id.input) EditText mInputField;
|
||||||
|
@Bind(R.id.cancel) Button mCancelButton;
|
||||||
|
@Bind(R.id.ok) Button mOkButton;
|
||||||
|
@Bind(R.id.number_grid) NumpadTimePicker mNumpad;
|
||||||
|
|
||||||
|
public NumpadTimePickerDialog() {
|
||||||
|
// Empty constructor required for dialog fragment.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We don't need to pass in an initial hour and minute for a new instance.
|
||||||
|
public static NumpadTimePickerDialog newInstance(OnTimeSetListener callback,
|
||||||
|
int hourOfDay, int minute, boolean is24HourMode) {
|
||||||
|
NumpadTimePickerDialog ret = new NumpadTimePickerDialog();
|
||||||
|
ret.initialize(callback, hourOfDay, minute, is24HourMode);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(OnTimeSetListener callback,
|
||||||
|
int hourOfDay, int minute, boolean is24HourMode) {
|
||||||
|
mCallback = callback;
|
||||||
|
mInitialHourOfDay = hourOfDay;
|
||||||
|
mInitialMinute = minute;
|
||||||
|
mIs24HourMode = is24HourMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnTimeSetListener(OnTimeSetListener callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_HOUR_OF_DAY)
|
||||||
|
&& savedInstanceState.containsKey(KEY_MINUTE)
|
||||||
|
&& savedInstanceState.containsKey(KEY_IS_24_HOUR_VIEW)) {
|
||||||
|
mInitialHourOfDay = savedInstanceState.getInt(KEY_HOUR_OF_DAY);
|
||||||
|
mInitialMinute = savedInstanceState.getInt(KEY_MINUTE);
|
||||||
|
mIs24HourMode = savedInstanceState.getBoolean(KEY_IS_24_HOUR_VIEW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
View view = inflater.inflate(R.layout.dialog_time_picker_numpad, container, false);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
mNumpad.setOnInputChangeListener(this);
|
||||||
|
|
||||||
|
//mNumpad.setTime(mInitialHourOfDay, mInitialMinute);
|
||||||
|
// TODO: Write numpad method set24HourMode() and use mIs24HourMode
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
if (mNumpad != null) {
|
||||||
|
outState.putInt(KEY_HOUR_OF_DAY, mNumpad.getHours());
|
||||||
|
outState.putInt(KEY_MINUTE, mNumpad.getMinutes());
|
||||||
|
outState.putBoolean(KEY_IS_24_HOUR_VIEW, mIs24HourMode);
|
||||||
|
//outState.putBoolean(KEY_DARK_THEME, mThemeDark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDigitInserted(String newStr) {
|
||||||
|
updateInputText(newStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDigitDeleted(String newStr) {
|
||||||
|
updateInputText(newStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDigitsCleared() {
|
||||||
|
updateInputText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.cancel)
|
||||||
|
void myCancel() {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.ok)
|
||||||
|
void ok() {
|
||||||
|
if (!mNumpad.checkTimeValid())
|
||||||
|
return;
|
||||||
|
mCallback.onTimeSet(mNumpad, mNumpad.getHours(), mNumpad.getMinutes());
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.backspace)
|
||||||
|
void backspace() {
|
||||||
|
mNumpad.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLongClick(R.id.backspace)
|
||||||
|
boolean longBackspace() {
|
||||||
|
mNumpad.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInputText(String inputText) {
|
||||||
|
mInputField.setText(inputText);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.philliphsu.clock2.editalarm;
|
||||||
|
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/12/2016.
|
||||||
|
*
|
||||||
|
* The callback interface used to indicate the user is done filling in
|
||||||
|
* the time (they clicked on the 'Set' button).
|
||||||
|
*/
|
||||||
|
public interface OnTimeSetListener {
|
||||||
|
/**
|
||||||
|
* @param viewGroup The view associated with this listener.
|
||||||
|
* @param hourOfDay The hour that was set.
|
||||||
|
* @param minute The minute that was set.
|
||||||
|
*/
|
||||||
|
void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute);
|
||||||
|
}
|
||||||
74
app/src/main/res/layout/content_grid_layout_numpad.xml
Normal file
74
app/src/main/res/layout/content_grid_layout_numpad.xml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?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/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="1"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/two"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="2"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/three"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="3"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/four"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="4"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/five"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="5"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/six"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="6"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/seven"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="7"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/eight"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="8"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/nine"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
android:text="9"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/zero"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
grid:layout_column="1"
|
||||||
|
android:text="0"/>
|
||||||
|
</merge>
|
||||||
20
app/src/main/res/layout/content_numpad_time_picker.xml
Normal file
20
app/src/main/res/layout/content_numpad_time_picker.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<include layout="@layout/content_grid_layout_numpad"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/leftAlt"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
grid:layout_column="0"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/rightAlt"
|
||||||
|
style="@style/grid_element_single"
|
||||||
|
grid:layout_rowWeight="1"
|
||||||
|
grid:layout_columnWeight="1"
|
||||||
|
grid:layout_column="2"/>
|
||||||
|
</merge>
|
||||||
65
app/src/main/res/layout/dialog_time_picker_numpad.xml
Normal file
65
app/src/main/res/layout/dialog_time_picker_numpad.xml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/backspace"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:src="@drawable/ic_backspace_24dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentTop="true"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/input"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?android:listDivider"
|
||||||
|
android:layout_below="@id/input"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/actions"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_alignParentBottom="true">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Cancel"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/ok"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Ok"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.philliphsu.clock2.editalarm.NumpadTimePicker
|
||||||
|
android:id="@+id/number_grid"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_below="@id/divider"
|
||||||
|
android:layout_above="@id/actions"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@ -21,4 +21,11 @@
|
|||||||
<item name="android:textSize">28sp</item>
|
<item name="android:textSize">28sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="grid_element_single">
|
||||||
|
<item name="android:layout_width">0dp</item>
|
||||||
|
<item name="android:layout_height">0dp</item>
|
||||||
|
<item name="android:background">?android:attr/selectableItemBackground</item>
|
||||||
|
<item name="android:textSize">20sp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user