diff --git a/ExaltedDice/AndroidManifest.xml b/ExaltedDice/AndroidManifest.xml index b15f1d2..5c0e541 100755 --- a/ExaltedDice/AndroidManifest.xml +++ b/ExaltedDice/AndroidManifest.xml @@ -5,7 +5,7 @@ android:versionCode="15" android:versionName="2.0" > - + diff --git a/ExaltedDice/res/drawable/timepicker_down_btn.xml b/ExaltedDice/res/drawable/timepicker_down_btn.xml new file mode 100755 index 0000000..61a252a --- /dev/null +++ b/ExaltedDice/res/drawable/timepicker_down_btn.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/ExaltedDice/res/drawable/timepicker_down_disabled.9.png b/ExaltedDice/res/drawable/timepicker_down_disabled.9.png new file mode 100755 index 0000000..596294b Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_down_disabled.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_down_disabled_focused.9.png b/ExaltedDice/res/drawable/timepicker_down_disabled_focused.9.png new file mode 100755 index 0000000..662cffd Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_down_disabled_focused.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_down_normal.9.png b/ExaltedDice/res/drawable/timepicker_down_normal.9.png new file mode 100755 index 0000000..f17e8f9 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_down_normal.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_down_pressed.9.png b/ExaltedDice/res/drawable/timepicker_down_pressed.9.png new file mode 100755 index 0000000..777bcf5 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_down_pressed.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_down_selected.9.png b/ExaltedDice/res/drawable/timepicker_down_selected.9.png new file mode 100755 index 0000000..b45db62 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_down_selected.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_input.xml b/ExaltedDice/res/drawable/timepicker_input.xml new file mode 100755 index 0000000..b811d4e --- /dev/null +++ b/ExaltedDice/res/drawable/timepicker_input.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/ExaltedDice/res/drawable/timepicker_input_disabled.9.png b/ExaltedDice/res/drawable/timepicker_input_disabled.9.png new file mode 100755 index 0000000..f73658e Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_input_disabled.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_input_normal.9.png b/ExaltedDice/res/drawable/timepicker_input_normal.9.png new file mode 100755 index 0000000..8032ada Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_input_normal.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_input_pressed.9.png b/ExaltedDice/res/drawable/timepicker_input_pressed.9.png new file mode 100755 index 0000000..30d8d5f Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_input_pressed.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_input_selected.9.png b/ExaltedDice/res/drawable/timepicker_input_selected.9.png new file mode 100755 index 0000000..874f18f Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_input_selected.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_up_btn.xml b/ExaltedDice/res/drawable/timepicker_up_btn.xml new file mode 100755 index 0000000..5428aee --- /dev/null +++ b/ExaltedDice/res/drawable/timepicker_up_btn.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/ExaltedDice/res/drawable/timepicker_up_disabled.9.png b/ExaltedDice/res/drawable/timepicker_up_disabled.9.png new file mode 100755 index 0000000..327b0b5 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_up_disabled.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_up_disabled_focused.9.png b/ExaltedDice/res/drawable/timepicker_up_disabled_focused.9.png new file mode 100755 index 0000000..4c96680 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_up_disabled_focused.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_up_normal.9.png b/ExaltedDice/res/drawable/timepicker_up_normal.9.png new file mode 100755 index 0000000..dcd26e0 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_up_normal.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_up_pressed.9.png b/ExaltedDice/res/drawable/timepicker_up_pressed.9.png new file mode 100755 index 0000000..7dac778 Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_up_pressed.9.png differ diff --git a/ExaltedDice/res/drawable/timepicker_up_selected.9.png b/ExaltedDice/res/drawable/timepicker_up_selected.9.png new file mode 100755 index 0000000..35dae8e Binary files /dev/null and b/ExaltedDice/res/drawable/timepicker_up_selected.9.png differ diff --git a/ExaltedDice/res/layout-v11/dice_selector.xml b/ExaltedDice/res/layout-v11/dice_selector.xml new file mode 100644 index 0000000..8fe85d2 --- /dev/null +++ b/ExaltedDice/res/layout-v11/dice_selector.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ExaltedDice/res/layout/dice_selector.xml b/ExaltedDice/res/layout/dice_selector.xml index 8fe85d2..9122535 100644 --- a/ExaltedDice/res/layout/dice_selector.xml +++ b/ExaltedDice/res/layout/dice_selector.xml @@ -4,19 +4,17 @@ android:layout_height="wrap_content" android:orientation="horizontal" > - + android:layout_height="wrap_content" /> - + android:layout_height="wrap_content" /> - diff --git a/ExaltedDice/res/layout/main.xml b/ExaltedDice/res/layout/main.xml index 9942ae1..b37e8e9 100755 --- a/ExaltedDice/res/layout/main.xml +++ b/ExaltedDice/res/layout/main.xml @@ -16,7 +16,7 @@ + android:layout_weight="2.5" > + + + + + + + + + + + \ No newline at end of file diff --git a/ExaltedDice/res/values/attrs.xml b/ExaltedDice/res/values/attrs.xml index 68a68fc..14a500b 100644 --- a/ExaltedDice/res/values/attrs.xml +++ b/ExaltedDice/res/values/attrs.xml @@ -1,10 +1,19 @@ - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/ExaltedDice.java b/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/ExaltedDice.java index 966e376..24724a2 100755 --- a/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/ExaltedDice.java +++ b/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/ExaltedDice.java @@ -44,16 +44,20 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic private ListView mListView; private NumberPicker mNumberPicker; private NumberPicker mDPicker; + private NumberPicker mModPicker; private Database mDb; private String mGameName; private long mGameId; private RollHistoryDatabaseAdapter mListAdapter; private SharedPreferences mSettings; private String[] mModValues; - private NumberPicker mModPicker; private ProgressBar mRollProgress; private View mRollButton; private boolean isRolling = false; + private com.TwentyCode.android.ExaltedDice.NumberPicker mCompatDPicker; + private com.TwentyCode.android.ExaltedDice.NumberPicker mCompatNumberPicker; + private com.TwentyCode.android.ExaltedDice.NumberPicker mCompatModPicker; + private boolean isCompat = false; /** * Applies the presets from the provided roll @@ -63,14 +67,66 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic private void applyRollPresets(long id) { ContentValues roll = mDb.getGameHistoryInfo(mGameName, (int) (id)); try{ - mNumberPicker.setValue(roll.getAsInteger(Database.KEY_NUMBER)); - mDPicker.setValue(parseD(roll.getAsString(Database.KEY_D_TYPE))); - mModPicker.setValue(parseMod(roll.getAsString(Database.KEY_MOD).replace("'", ""))); + if(isCompat){ + mCompatNumberPicker.setValue(roll.getAsInteger(Database.KEY_NUMBER)); + mCompatDPicker.setValue(parseD(roll.getAsString(Database.KEY_D_TYPE))); + mCompatModPicker.setValue(parseMod(roll.getAsString(Database.KEY_MOD).replace("'", ""))); + } else { + mNumberPicker.setValue(roll.getAsInteger(Database.KEY_NUMBER)); + mDPicker.setValue(parseD(roll.getAsString(Database.KEY_D_TYPE))); + mModPicker.setValue(parseMod(roll.getAsString(Database.KEY_MOD).replace("'", ""))); + } } catch(NullPointerException e){ - mModPicker.setValue(parseMod("+0")); + if(isCompat) + mCompatModPicker.setValue(parseMod("+0")); + else + mModPicker.setValue(parseMod("+0")); } } + /** + * Initializes compat pickers for api < 11 + * @author ricky barrette + */ + private void initCompatPickers() { + isCompat = true; + + mCompatDPicker = (com.TwentyCode.android.ExaltedDice.NumberPicker) findViewById(R.id.d_Picker); + mCompatDPicker.setDisplayedValues(mDiceValues); + mCompatDPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + + mCompatNumberPicker = (com.TwentyCode.android.ExaltedDice.NumberPicker) findViewById(R.id.number_Picker); + mCompatNumberPicker.setRange(1, 999); + mCompatNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + + mCompatModPicker = (com.TwentyCode.android.ExaltedDice.NumberPicker) findViewById(R.id.mod_Picker); + mCompatModPicker.setDisplayedValues(mModValues); + mCompatModPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + } + + /** + * Initializes native number pickers api > 11 + * @author ricky barrette + */ + private void initPickers() { + mDPicker = (NumberPicker) findViewById(R.id.d_Picker); + mDPicker.setMinValue(0); + mDPicker.setMaxValue(mDiceValues.length -1); + mDPicker.setDisplayedValues(mDiceValues); + mDPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + + mNumberPicker = (NumberPicker) findViewById(R.id.number_Picker); + mNumberPicker.setMaxValue(999); + mNumberPicker.setMinValue(1); + mNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + + mModPicker = (NumberPicker) findViewById(R.id.mod_Picker); + mModPicker.setMinValue(0); + mModPicker.setMaxValue(mModValues.length -1); + mModPicker.setDisplayedValues(mModValues); + mModPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + } + /** * also implemented OnClickListener * @@ -113,13 +169,21 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic Log.i(TAG, "onCreate()"); setContentView(R.layout.main); + mSettings = getSharedPreferences(Settings.SETTINGS, Context.MODE_WORLD_WRITEABLE); + + mDiceValues = getResources().getStringArray(R.array.dice_types); + mModValues = getResources().getStringArray(R.array.mods); + /* * The following is for api 11 and up + * else use compat methods */ if(Integer.valueOf(android.os.Build.VERSION.SDK) > 11){ ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); - } + initPickers(); + } else + initCompatPickers(); Intent i = this.getIntent(); if(i != null) @@ -129,32 +193,10 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic this.setTitle(mGameName); } - mSettings = getSharedPreferences(Settings.SETTINGS, Context.MODE_WORLD_WRITEABLE); - - mDiceValues = getResources().getStringArray(R.array.dice_types); - mModValues = getResources().getStringArray(R.array.mods); - mListView = (ListView) findViewById(R.id.list); mListView.setOnItemClickListener(this); mListView.setStackFromBottom(true); - mDPicker = (NumberPicker) findViewById(R.id.d_Picker); - mDPicker.setMinValue(0); - mDPicker.setMaxValue(mDiceValues.length -1); - mDPicker.setDisplayedValues(mDiceValues); - mDPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); - - mNumberPicker = (NumberPicker) findViewById(R.id.number_Picker); - mNumberPicker.setMaxValue(999); - mNumberPicker.setMinValue(1); - mNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); - - mModPicker = (NumberPicker) findViewById(R.id.mod_Picker); - mModPicker.setMinValue(0); - mModPicker.setMaxValue(mModValues.length -1); - mModPicker.setDisplayedValues(mModValues); - mModPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); - mRollProgress = (ProgressBar) findViewById(R.id.roll_progress); mRollButton = findViewById(R.id.roll_button); @@ -183,6 +225,40 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic return true; } + @Override + public void onDatabaseInsertComplete() { + isRolling = false; + this.runOnUiThread(new Runnable(){ + @Override + public void run(){ + mRollProgress.setVisibility(View.GONE); + mRollButton.setEnabled(true); + refresh(); + } + }); + } + + @Override + public void onDatabaseUpgrade() { + //do nothing + } + + @Override + public void onDatabaseUpgradeComplete() { + // do nothing + + } + + @Override + public void onDeletionComplete() { + this.runOnUiThread(new Runnable(){ + @Override + public void run(){ + refresh(); + } + }); + } + /** * rolls same amount of dice as previous roll * @author ricky barrette @@ -220,29 +296,31 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic return super.onOptionsItemSelected(item); } } - - /** - * (non-Javadoc) - * @see android.app.Activity#onPause() - */ - @Override - protected void onStop() { - mDb.close(); - super.onStop(); + + @Override + public void onRestoreComplete() { + // do nothing } + /** - * resorts application state after rotation - * @author ricky barrette - */ - @Override - public void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - mDPicker.setValue(savedInstanceState.getInt("d")); - mNumberPicker.setValue(savedInstanceState.getInt("number")); - mModPicker.setValue(savedInstanceState.getInt("mod")); - } + * resorts application state after rotation + * @author ricky barrette + */ + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if(isCompat){ + mCompatDPicker.setCurrent(savedInstanceState.getInt("d")); + mCompatNumberPicker.setCurrent(savedInstanceState.getInt("number")); + mCompatModPicker.setCurrent(savedInstanceState.getInt("mod")); + } else { + mDPicker.setValue(savedInstanceState.getInt("d")); + mNumberPicker.setValue(savedInstanceState.getInt("number")); + mModPicker.setValue(savedInstanceState.getInt("mod")); + } + } - /** + /** * (non-Javadoc) * @see android.app.Activity#onResume() */ @@ -252,15 +330,41 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic applyRollPresets(mDb.getGameRollCount(mGameId)); if(mSettings.getBoolean(Settings.KEY_ROLL_MOD, true)){ - mModPicker.setVisibility(View.VISIBLE); + if(isCompat) + mCompatModPicker.setVisibility(View.VISIBLE); + else + mModPicker.setVisibility(View.VISIBLE); } else { - mModPicker.setVisibility(View.GONE); - mModPicker.setValue(parseMod("+0")); + if(isCompat){ + mCompatModPicker.setVisibility(View.GONE); + mCompatModPicker.setValue(parseMod("+0")); + } else { + mModPicker.setVisibility(View.GONE); + mModPicker.setValue(parseMod("+0")); + } } super.onResume(); } + + /** + * saves application state before rotation + * @author ricky barrette + */ + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + if(isCompat){ + savedInstanceState.putInt("d", mCompatDPicker.getCurrent()); + savedInstanceState.putInt("number", mCompatNumberPicker.getCurrent()); + savedInstanceState.putInt("mod", mCompatModPicker.getCurrent()); + } else { + savedInstanceState.putInt("d", mDPicker.getValue()); + savedInstanceState.putInt("number", mNumberPicker.getValue()); + savedInstanceState.putInt("mod", mModPicker.getValue()); + } + super.onSaveInstanceState(savedInstanceState); + } - /** + /** * (non-Javadoc) * @see android.app.Activity#onStart() */ @@ -273,15 +377,13 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic } /** - * saves application state before rotation - * @author ricky barrette + * (non-Javadoc) + * @see android.app.Activity#onPause() */ @Override - public void onSaveInstanceState(Bundle savedInstanceState) { - savedInstanceState.putInt("d", mDPicker.getValue()); - savedInstanceState.putInt("number", mNumberPicker.getValue()); - savedInstanceState.putInt("mod", mModPicker.getValue()); - super.onSaveInstanceState(savedInstanceState); + protected void onStop() { + mDb.close(); + super.onStop(); } /** @@ -296,8 +398,8 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic return i; return 0; } - - /** + + /** * Parses the string mod to the appropriate value * @param mod * @return value for d picker @@ -332,7 +434,7 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic }); builder.show(); } - + /** * Refreshes the list view * @author ricky barrette @@ -366,7 +468,10 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic resultsString.append(getString(R.string.total)+ total); if(mSettings.getBoolean(Settings.KEY_ROLL_MOD, true)) - resultsString.append(getString(R.string.total_plus_mod)+ (total + Integer.parseInt(mModValues[mModPicker.getValue()].replace("+", "")))); + if(isCompat) + resultsString.append(getString(R.string.total_plus_mod)+ (total + Integer.parseInt(mCompatModPicker.getValue().replace("+", "")))); + else + resultsString.append(getString(R.string.total_plus_mod)+ (total + Integer.parseInt(mModValues[mModPicker.getValue()].replace("+", "")))); if(mSettings.getBoolean(Settings.KEY_CALC_SUCCESSES, true)) resultsString.append(getString(R.string.sucesses)+ successes(roll)); @@ -399,11 +504,20 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic int rollId = mDb.getGameRollCount(mGameId) +1; ContentValues roll = new ContentValues(); - roll.put(Database.KEY_D_TYPE, mDiceValues[mDPicker.getValue()]); - roll.put(Database.KEY_NUMBER, mNumberPicker.getValue()); - roll.putAll(results(mNumberPicker.getValue())); - roll.put(Database.KEY_MOD, DatabaseUtils.sqlEscapeString(mModValues[mModPicker.getValue()])); + if(isCompat){ + roll.put(Database.KEY_D_TYPE, mCompatDPicker.getValue()); + roll.put(Database.KEY_NUMBER, mCompatNumberPicker.getCurrent()); + roll.putAll(results(mCompatNumberPicker.getCurrent())); + roll.put(Database.KEY_MOD, DatabaseUtils.sqlEscapeString(mCompatModPicker.getValue())); + } else{ + roll.put(Database.KEY_D_TYPE, mDiceValues[mDPicker.getValue()]); + roll.put(Database.KEY_NUMBER, mNumberPicker.getValue()); + roll.putAll(results(mNumberPicker.getValue())); + roll.put(Database.KEY_MOD, DatabaseUtils.sqlEscapeString(mModValues[mModPicker.getValue()])); + } + + mDb.updateGame(mGameId, mGameName, roll, rollId); } @@ -422,7 +536,10 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic int[] roll = new int[times]; Random random = new Random(); for (int i = 0; i < times; i++) { - roll[i] = random.nextInt(Integer.parseInt(mDiceValues[mDPicker.getValue()].substring(1))) + 1; + if(isCompat) + roll[i] = random.nextInt(Integer.parseInt(mCompatDPicker.getValue().substring(1))) + 1; + else + roll[i] = random.nextInt(Integer.parseInt(mDiceValues[mDPicker.getValue()].substring(1))) + 1; } return roll; } @@ -470,43 +587,4 @@ public class ExaltedDice extends Activity implements OnClickListener, OnItemClic Vibrator vib = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); vib.vibrate(milliseconds); } - - @Override - public void onDatabaseUpgradeComplete() { - // do nothing - - } - - @Override - public void onDeletionComplete() { - this.runOnUiThread(new Runnable(){ - @Override - public void run(){ - refresh(); - } - }); - } - - @Override - public void onRestoreComplete() { - // do nothing - } - - @Override - public void onDatabaseUpgrade() { - //do nothing - } - - @Override - public void onDatabaseInsertComplete() { - isRolling = false; - this.runOnUiThread(new Runnable(){ - @Override - public void run(){ - mRollProgress.setVisibility(View.GONE); - mRollButton.setEnabled(true); - refresh(); - } - }); - } } \ No newline at end of file diff --git a/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/NumberPicker.java b/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/NumberPicker.java new file mode 100755 index 0000000..8f7409a --- /dev/null +++ b/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/NumberPicker.java @@ -0,0 +1,488 @@ +/** + * Number Picker.java + * + * @author Google + * @author ricky barrette + * @author Twenty Codes + * + * Copyright (C) 2012 Rick barrette + * Copyright (C) 2012 Twenty Codes + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.TwentyCode.android.ExaltedDice; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Handler; +import android.text.InputFilter; +import android.text.InputType; +import android.text.Spanned; +import android.text.method.NumberKeyListener; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.view.View.OnLongClickListener; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * This class has been pulled from the Android platform source code, its an internal widget that hasn't been + * made public so its included in the project in this fashion for use with the preferences screen; I have made + * a few slight modifications to the code here, I simply put a MAX and MIN default in the code but these values + * can still be set publicly by calling code. + * + * @author Google + */ +public class NumberPicker extends LinearLayout implements OnClickListener, OnFocusChangeListener, OnLongClickListener { + + /** + * This class is used to filter the input from a keyboard + */ + private class NumberPickerInputFilter implements InputFilter { + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + if (mDisplayedValues == null) { + return mNumberInputFilter.filter(source, start, end, dest, dstart, dend); + } + CharSequence filtered = String.valueOf(source.subSequence(start, end)); + String result = String.valueOf(dest.subSequence(0, dstart)) + + filtered + + dest.subSequence(dend, dest.length()); + String str = String.valueOf(result).toLowerCase(); + for (String val : mDisplayedValues) { + val = val.toLowerCase(); + if (val.startsWith(str)) { + return filtered; + } + } + return ""; + } + } + + /** + * This class + */ + private class NumberRangeKeyListener extends NumberKeyListener { + + @Override + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + + CharSequence filtered = super.filter(source, start, end, dest, dstart, dend); + if (filtered == null) { + filtered = source.subSequence(start, end); + } + + String result = String.valueOf(dest.subSequence(0, dstart)) + + filtered + + dest.subSequence(dend, dest.length()); + + if ("".equals(result)) { + return result; + } + int val = getSelectedPos(result); + + /* Ensure the user can't type in a value greater + * than the max allowed. We have to allow less than min + * as the user might want to delete some numbers + * and then type a new number. + */ + if (val > mEnd) { + return ""; + } else { + return filtered; + } + } + + @Override + protected char[] getAcceptedChars() { + return DIGIT_CHARACTERS; + } + + // XXX This doesn't allow for range limits when controlled by a + // soft input method! + public int getInputType() { + return InputType.TYPE_CLASS_NUMBER; + } + } + + /** + * A simple interface to notify a change has been made + */ + public interface OnChangedListener { + /** + * Called when a change has been made + * @param picker + * @param oldVal + * @param newVal + */ + void onChanged(NumberPicker picker, int oldVal, int newVal); + } + + private static final int DEFAULT_MAX = 200; + private static final int DEFAULT_MIN = 0; + private static final int DEFAULT_VALUE = 0; + private static final boolean DEFAULT_WRAP = true; + private final Handler mHandler; + private final EditText mText; + private final InputFilter mNumberInputFilter; + + private final Runnable mRunnable = new Runnable() { + public void run() { + if (mIncrement) { + changeCurrent(mCurrent + 1); + mHandler.postDelayed(this, mSpeed); + } else if (mDecrement) { + changeCurrent(mCurrent - 1); + mHandler.postDelayed(this, mSpeed); + } + } + }; + + private String[] mDisplayedValues; + protected int mStart; + protected int mEnd; + protected int mCurrent; + protected int mPrevious; + private OnChangedListener mListener; + private long mSpeed = 300; + private boolean mIncrement; + private boolean mDecrement; + private NumberPickerButton mIncrementButton; + private NumberPickerButton mDecrementButton; + private boolean mWrap; + private static final char[] DIGIT_CHARACTERS = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + /** + * Creates a new NumberPicker + * @param context + */ + public NumberPicker(Context context) { + this(context, null); + } + + /** + * Creates a new NumberPicker + * @param context + * @param attrs + * @author ricky barrette + */ + public NumberPicker(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Creates a new NumberPicker + * @param context + * @param attrs + * @param defStyle + * @author ricky barrette + */ + public NumberPicker(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + + setOrientation(VERTICAL); + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.number_picker, this, true); + mHandler = new Handler(); + InputFilter inputFilter = new NumberPickerInputFilter(); + mNumberInputFilter = new NumberRangeKeyListener(); + mIncrementButton = (NumberPickerButton) findViewById(R.id.increment); + mIncrementButton.setOnClickListener(this); + mIncrementButton.setOnLongClickListener(this); + mIncrementButton.setNumberPicker(this); + mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement); + mDecrementButton.setOnClickListener(this); + mDecrementButton.setOnLongClickListener(this); + mDecrementButton.setNumberPicker(this); + + mText = (EditText) findViewById(R.id.timepicker_input); + mText.setOnFocusChangeListener(this); + mText.setFilters(new InputFilter[] {inputFilter}); + mText.setRawInputType(InputType.TYPE_CLASS_NUMBER); + + if (!isEnabled()) { + setEnabled(false); + } + + TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.numberpicker ); + mStart = a.getInt( R.styleable.numberpicker_startRange, DEFAULT_MIN ); + mEnd = a.getInt( R.styleable.numberpicker_endRange, DEFAULT_MAX ); + mWrap = a.getBoolean( R.styleable.numberpicker_wrap, DEFAULT_WRAP ); + mCurrent = a.getInt( R.styleable.numberpicker_defaultValue, DEFAULT_VALUE ); + mCurrent = Math.max( mStart, Math.min( mCurrent, mEnd ) ); + mText.setText( "" + mCurrent ); + } + + public void cancelDecrement() { + mDecrement = false; + } + + public void cancelIncrement() { + mIncrement = false; + } + + + protected void changeCurrent(int current) { + // Wrap around the values if we go past the start or end + if (current > mEnd) { + current = mWrap ? mStart : mEnd; + } else if (current < mStart) { + current = mWrap ? mEnd : mStart; + } + mPrevious = mCurrent; + mCurrent = current; + + notifyChange(); + updateView(); + } + + /** + * @return the current value. + */ + public int getCurrent() { + return mCurrent; + } + + private int getSelectedPos(String str) { + if (mDisplayedValues == null) { + return Integer.parseInt(str); + } else { + for (int i = 0; i < mDisplayedValues.length; i++) { + + /* Don't force the user to type in jan when ja will do */ + str = str.toLowerCase(); + if (mDisplayedValues[i].toLowerCase().startsWith(str)) { + return mStart + i; + } + } + + /* The user might have typed in a number into the month field i.e. + * 10 instead of OCT so support that too. + */ + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + + /* Ignore as if it's not a number we don't care */ + } + } + return mStart; + } + + public String getValue() { + return this.mDisplayedValues[mCurrent]; + } + + protected void notifyChange() { + if (mListener != null) { + mListener.onChanged(this, mPrevious, mCurrent); + } + } + + public void onClick(View v) { + validateInput(mText); + if (!mText.hasFocus()) mText.requestFocus(); + + // now perform the increment/decrement + if (R.id.increment == v.getId()) { + changeCurrent(mCurrent + 1); + } else if (R.id.decrement == v.getId()) { + changeCurrent(mCurrent - 1); + } + } + + public void onFocusChange(View v, boolean hasFocus) { + + /* When focus is lost check that the text field + * has valid values. + */ + if (!hasFocus) { + validateInput(v); + } + } + + /** + * We start the long click here but rely on the {@link NumberPickerButton} + * to inform us when the long click has ended. + */ + public boolean onLongClick(View v) { + + /* The text view may still have focus so clear it's focus which will + * trigger the on focus changed and any typed values to be pulled. + */ + mText.clearFocus(); + mText.requestFocus(); + if (R.id.increment == v.getId()) { + mIncrement = true; + mHandler.post(mRunnable); + } else if (R.id.decrement == v.getId()) { + mDecrement = true; + mHandler.post(mRunnable); + } + + return true; + } + + /** + * Sets the current index + * @param index + */ + public void setCurrent(int index) { + mCurrent = index; + updateView(); + } + + /** + * Sets the current index and notifies listeners + * @param index + */ + public void setCurrentAndNotify(int index) { + mCurrent = index; + notifyChange(); + updateView(); + } + + /** + * Sets the values to display + * @param values + * @author ricky barrette + */ + public void setDisplayedValues(String[] values) { + this.mDisplayedValues = values; + this.mStart = 0; + this.mEnd = values.length -1; + notifyChange(); + updateView(); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mIncrementButton.setEnabled(enabled); + mDecrementButton.setEnabled(enabled); + mText.setEnabled(enabled); + } + + public void setOnChangeListener(OnChangedListener listener) { + mListener = listener; + } + + /** + * Set the range of numbers allowed for the number picker. The current + * value will be automatically set to the start. + * + * @param start the start of the range (inclusive) + * @param end the end of the range (inclusive) + */ + public void setRange(int start, int end) { + mStart = start; + mEnd = end; + mCurrent = start; + updateView(); + } + + /** + * Set the range of numbers allowed for the number picker. The current + * value will be automatically set to the start. Also provide a mapping + * for values used to display to the user. + * + * @param start the start of the range (inclusive) + * @param end the end of the range (inclusive) + * @param displayedValues the values displayed to the user. + */ + public void setRange(int start, int end, String[] displayedValues) { + mDisplayedValues = displayedValues; + mStart = start; + mEnd = end; + mCurrent = start; + updateView(); + } + + /** + * The speed (in milliseconds) at which the numbers will scroll + * when the the +/- buttons are longpressed. Default is 300ms. + */ + public void setSpeed(long speed) { + mSpeed = speed; + } + + /** + * Sets the current displayed value by it's index + * @param index + * @author ricky barrette + */ + public void setValue(int index) { + mCurrent = index; + notifyChange(); + updateView(); + } + + /** + * Specify if numbers should wrap after the edge has been reached. + * + * @param wrap values + */ + public void setWrap( boolean wrap ) { + mWrap = wrap; + } + + protected void updateView() { + + /* If we don't have displayed values then use the + * current number else find the correct value in the + * displayed values for the current number. + */ + if (mDisplayedValues == null) { + mText.setText(mCurrent+""); + } else { + mText.setText(mDisplayedValues[mCurrent]); + } + mText.setSelection(mText.getText().length()); + +// mText.resizeText(); + } + + private void validateCurrentView(CharSequence str) { + int val = getSelectedPos(str.toString()); + if ((val >= mStart) && (val <= mEnd)) { + if (mCurrent != val) { + mPrevious = mCurrent; + mCurrent = val; + notifyChange(); + } + } + updateView(); + } + + private void validateInput(View v) { + String str = String.valueOf(((TextView) v).getText()); + if ("".equals(str)) { + + // Restore to the old value as we don't allow empty values + updateView(); + } else { + + // Check the new value and ensure it's in range + validateCurrentView(str); + } + } +} diff --git a/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/NumberPickerButton.java b/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/NumberPickerButton.java new file mode 100755 index 0000000..ea9675d --- /dev/null +++ b/ExaltedDice/src/com/TwentyCode/android/ExaltedDice/NumberPickerButton.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.TwentyCode.android.ExaltedDice; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.widget.ImageButton; + +/** + * This class exists purely to cancel long click events. + */ +public class NumberPickerButton extends ImageButton { + + private NumberPicker mNumberPicker; + + public NumberPickerButton(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + } + + public NumberPickerButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NumberPickerButton(Context context) { + super(context); + } + + public void setNumberPicker(NumberPicker picker) { + mNumberPicker = picker; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + cancelLongpressIfRequired(event); + return super.onTouchEvent(event); + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + cancelLongpressIfRequired(event); + return super.onTrackballEvent(event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER) + || (keyCode == KeyEvent.KEYCODE_ENTER)) { + cancelLongpress(); + } + return super.onKeyUp(keyCode, event); + } + + private void cancelLongpressIfRequired(MotionEvent event) { + if ((event.getAction() == MotionEvent.ACTION_CANCEL) + || (event.getAction() == MotionEvent.ACTION_UP)) { + cancelLongpress(); + } + } + + private void cancelLongpress() { + if (R.id.increment == getId()) { + mNumberPicker.cancelIncrement(); + } else if (R.id.decrement == getId()) { + mNumberPicker.cancelDecrement(); + } + } +} diff --git a/project.properties b/project.properties new file mode 100644 index 0000000..cd9c350 --- /dev/null +++ b/project.properties @@ -0,0 +1,13 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Indicates whether an apk should be generated for each density. +split.density=false +# Project target. +target=android-15