diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..be2e532
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: android
+android:
+ components:
+ - build-tools-20.0.0
+ - android-21
+ - extra-android-m2repository
+env:
+ matrix:
+ - ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a
+before_script:
+ # Create and start emulator
+ - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
+ - emulator -avd test -no-skin -no-audio -no-window &
+ - adb wait-for-device
+ - adb shell input keyevent 82 &
+
+script: ./gradlew connectedAndroidTest
diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml
index f8b6c4d..2dd0a19 100644
--- a/demo/src/main/AndroidManifest.xml
+++ b/demo/src/main/AndroidManifest.xml
@@ -1,23 +1,23 @@
+ package="eu.inmite.demo.dialogs"
+ android:versionCode="1"
+ android:versionName="1.0">
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/java/eu/inmite/demo/dialogs/JayneHatDialogFragment.java b/demo/src/main/java/eu/inmite/demo/dialogs/JayneHatDialogFragment.java
index f6140a5..9886082 100644
--- a/demo/src/main/java/eu/inmite/demo/dialogs/JayneHatDialogFragment.java
+++ b/demo/src/main/java/eu/inmite/demo/dialogs/JayneHatDialogFragment.java
@@ -30,26 +30,26 @@
*/
public class JayneHatDialogFragment extends SimpleDialogFragment {
- public static String TAG = "jayne";
+ public static String TAG = "jayne";
- public static void show(FragmentActivity activity) {
- new JayneHatDialogFragment().show(activity.getSupportFragmentManager(), TAG);
- }
+ public static void show(FragmentActivity activity) {
+ new JayneHatDialogFragment().show(activity.getSupportFragmentManager(), TAG);
+ }
- @Override
- public BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
- builder.setTitle("Jayne's hat");
- builder.setView(LayoutInflater.from(getActivity()).inflate(R.layout.item_jayne_hat, null));
- builder.setPositiveButton("I want one", new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- ISimpleDialogListener listener = getDialogListener();
- if (listener != null) {
- listener.onPositiveButtonClicked(0);
- }
- dismiss();
- }
- });
- return builder;
- }
+ @Override
+ public BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
+ builder.setTitle("Jayne's hat");
+ builder.setView(LayoutInflater.from(getActivity()).inflate(R.layout.item_jayne_hat, null));
+ builder.setPositiveButton("I want one", new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ISimpleDialogListener listener = getDialogListener();
+ if (listener != null) {
+ listener.onPositiveButtonClicked(0);
+ }
+ dismiss();
+ }
+ });
+ return builder;
+ }
}
diff --git a/demo/src/main/java/eu/inmite/demo/dialogs/MyActivity.java b/demo/src/main/java/eu/inmite/demo/dialogs/MyActivity.java
index a9531e6..0a0d057 100644
--- a/demo/src/main/java/eu/inmite/demo/dialogs/MyActivity.java
+++ b/demo/src/main/java/eu/inmite/demo/dialogs/MyActivity.java
@@ -16,30 +16,22 @@
package eu.inmite.demo.dialogs;
+import java.text.DateFormat;
+import java.util.Date;
+
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.Toast;
-import java.text.DateFormat;
-import java.util.Date;
-
-import eu.inmite.android.lib.dialogs.DatePickerDialogFragment;
-import eu.inmite.android.lib.dialogs.IDateDialogListener;
-import eu.inmite.android.lib.dialogs.IListDialogListener;
-import eu.inmite.android.lib.dialogs.ISimpleDialogCancelListener;
-import eu.inmite.android.lib.dialogs.ISimpleDialogListener;
-import eu.inmite.android.lib.dialogs.ListDialogFragment;
-import eu.inmite.android.lib.dialogs.ProgressDialogFragment;
-import eu.inmite.android.lib.dialogs.SimpleDialogFragment;
-import eu.inmite.android.lib.dialogs.TimePickerDialogFragment;
+import eu.inmite.android.lib.dialogs.*;
public class MyActivity extends FragmentActivity implements
- ISimpleDialogListener,
- IDateDialogListener,
- ISimpleDialogCancelListener,
- IListDialogListener {
+ ISimpleDialogListener,
+ IDateDialogListener,
+ ISimpleDialogCancelListener,
+ IListDialogListener {
public static final int THEME_DEFAULT_DARK = 0;
@@ -64,48 +56,48 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public void onClick(View v) {
SimpleDialogFragment.createBuilder(c, getSupportFragmentManager())
- .setMessage(R.string.message_1).show();
+ .setMessage(R.string.message_1).show();
}
});
findViewById(R.id.message_title_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SimpleDialogFragment.createBuilder(c, getSupportFragmentManager())
- .setTitle(R.string.title).setMessage(R.string.message_2).show();
+ .setTitle(R.string.title).setMessage(R.string.message_2).show();
}
});
findViewById(R.id.message_title_buttons_dialog)
- .setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- SimpleDialogFragment.createBuilder(c, getSupportFragmentManager())
- .setTitle(R.string.title)
- .setMessage(R.string.message_3)
- .setPositiveButtonText(R.string.positive_button)
- .setNegativeButtonText(R.string.negative_button).setRequestCode(42)
- .setTag("custom-tag")
- .show();
- }
- });
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ SimpleDialogFragment.createBuilder(c, getSupportFragmentManager())
+ .setTitle(R.string.title)
+ .setMessage(R.string.message_3)
+ .setPositiveButtonText(R.string.positive_button)
+ .setNegativeButtonText(R.string.negative_button).setRequestCode(42)
+ .setTag("custom-tag")
+ .show();
+ }
+ });
findViewById(R.id.progress_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ProgressDialogFragment.createBuilder(c, getSupportFragmentManager())
- .setMessage(R.string.message_4)
- .setRequestCode(REQUEST_PROGRESS)
- .setTitle(R.string.app_name)
- .show();
+ .setMessage(R.string.message_4)
+ .setRequestCode(REQUEST_PROGRESS)
+ .setTitle(R.string.app_name)
+ .show();
}
});
findViewById(R.id.list_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ListDialogFragment
- .createBuilder(c, getSupportFragmentManager())
- .setTitle("Your favorite character:")
- .setItems(new String[]{"Jayne", "Malcolm", "Kaylee",
- "Wash", "Zoe", "River"})
- .show();
+ .createBuilder(c, getSupportFragmentManager())
+ .setTitle("Your favorite character:")
+ .setItems(new String[]{"Jayne", "Malcolm", "Kaylee",
+ "Wash", "Zoe", "River"})
+ .show();
}
});
@@ -139,32 +131,48 @@ public void onClick(View v) {
setCurrentTheme(THEME_CUSTOM_LIGHT);
}
});
- findViewById(R.id.time_picker).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- TimePickerDialogFragment
- .createBuilder(MyActivity.this, getSupportFragmentManager())
- .setDate(new Date())
- .set24hour(true)
- .setPositiveButtonText(android.R.string.ok)
- .setNegativeButtonText(android.R.string.cancel)
- .setRequestCode(13)
- .show();
- }
- });
- findViewById(R.id.date_picker).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- DatePickerDialogFragment
- .createBuilder(MyActivity.this, getSupportFragmentManager())
- .setDate(new Date())
- .set24hour(true)
- .setPositiveButtonText(android.R.string.ok)
- .setNegativeButtonText(android.R.string.cancel)
- .setRequestCode(12)
- .show();
- }
- });
+ findViewById(R.id.time_picker).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TimePickerDialogFragment
+ .createBuilder(MyActivity.this, getSupportFragmentManager())
+ .setDate(new Date())
+ .set24hour(true)
+ .setPositiveButtonText(android.R.string.ok)
+ .setNegativeButtonText(android.R.string.cancel)
+ .setRequestCode(13)
+ .show();
+ }
+ });
+ findViewById(R.id.date_picker).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ DatePickerDialogFragment
+ .createBuilder(MyActivity.this, getSupportFragmentManager())
+ .setDate(new Date())
+ .set24hour(true)
+ .setPositiveButtonText(android.R.string.ok)
+ .setNegativeButtonText(android.R.string.cancel)
+ .setRequestCode(12)
+ .show();
+ }
+ });
+
+ findViewById(R.id.number_picker).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ NumberPickerDialogFragment
+ .createBuilder(MyActivity.this, getSupportFragmentManager())
+ .setTitle("Pick a number:")
+ .setNumber(5)
+ .setMaxValue(20)
+ .setMinValue(0)
+ .setPositiveButtonText(android.R.string.ok)
+ .setNegativeButtonText(android.R.string.cancel)
+ .setRequestCode(14)
+ .show();
+ }
+ });
}
// IListDialogListener
@@ -216,30 +224,30 @@ public void onNeutralButtonClicked(int requestCode) {
// IDateDialogListener
@Override
- public void onNegativeButtonClicked(int resultCode, Date date) {
- String text="";
- if (resultCode==12) {
- text="Date ";
- } else if (resultCode==13) {
- text="Time ";
- }
-
- DateFormat dateFormat= DateFormat.getDateInstance(DateFormat.DEFAULT);
- Toast.makeText(this,text+"Cancelled "+ dateFormat.format(date),Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onPositiveButtonClicked(int resultCode, Date date) {
- String text="";
- if (resultCode==12) {
- text="Date ";
- } else if (resultCode==13) {
- text="Time ";
- }
-
- DateFormat dateFormat= DateFormat.getDateTimeInstance();
- Toast.makeText(this,text+ "Success! "+ dateFormat.format(date),Toast.LENGTH_SHORT).show();
- }
+ public void onNegativeButtonClicked(int resultCode, Date date) {
+ String text = "";
+ if (resultCode == 12) {
+ text = "Date ";
+ } else if (resultCode == 13) {
+ text = "Time ";
+ }
+
+ DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT);
+ Toast.makeText(this, text + "Cancelled " + dateFormat.format(date), Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onPositiveButtonClicked(int resultCode, Date date) {
+ String text = "";
+ if (resultCode == 12) {
+ text = "Date ";
+ } else if (resultCode == 13) {
+ text = "Time ";
+ }
+
+ DateFormat dateFormat = DateFormat.getDateTimeInstance();
+ Toast.makeText(this, text + "Success! " + dateFormat.format(date), Toast.LENGTH_SHORT).show();
+ }
private void setCurrentTheme(int theme) {
Intent i = new Intent(c, MyActivity.class);
diff --git a/demo/src/main/res/layout/item_jayne_hat.xml b/demo/src/main/res/layout/item_jayne_hat.xml
index e707e29..a24f340 100644
--- a/demo/src/main/res/layout/item_jayne_hat.xml
+++ b/demo/src/main/res/layout/item_jayne_hat.xml
@@ -1,6 +1,6 @@
\ No newline at end of file
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/jayne_hat"
+ android:padding="@dimen/grid_2" />
\ No newline at end of file
diff --git a/demo/src/main/res/layout/item_list.xml b/demo/src/main/res/layout/item_list.xml
index 83ded4a..937f72b 100644
--- a/demo/src/main/res/layout/item_list.xml
+++ b/demo/src/main/res/layout/item_list.xml
@@ -1,7 +1,8 @@
-
+ style="@style/ListItem">
+
+
\ No newline at end of file
diff --git a/demo/src/main/res/layout/main.xml b/demo/src/main/res/layout/main.xml
index e81d0d5..00ac274 100644
--- a/demo/src/main/res/layout/main.xml
+++ b/demo/src/main/res/layout/main.xml
@@ -1,81 +1,86 @@
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
-
-
+
+
+
+
+
+
diff --git a/demo/src/main/res/values-v11/theme.xml b/demo/src/main/res/values-v11/theme.xml
index ed04a03..7617996 100644
--- a/demo/src/main/res/values-v11/theme.xml
+++ b/demo/src/main/res/values-v11/theme.xml
@@ -1,22 +1,22 @@
-
+
-
+
-
+
-
+
\ No newline at end of file
diff --git a/demo/src/main/res/values/colors.xml b/demo/src/main/res/values/colors.xml
index 424f0e1..30c4790 100644
--- a/demo/src/main/res/values/colors.xml
+++ b/demo/src/main/res/values/colors.xml
@@ -1,23 +1,23 @@
-
- #333333
- #f3a71e
- #f3a71e
- #333333
- @android:color/transparent
- #99f3a71e
- #4Cf3a71e
- #25000000
- #FFA500
-
- #FFFFFF
- #F8A500
- #F8A500
- #FFFFFF
- @android:color/transparent
- #99F8A500
- #4CF8A500
- #12FFFFFF
- #C35817
+
+ #333333
+ #f3a71e
+ #f3a71e
+ #333333
+ @android:color/transparent
+ #99f3a71e
+ #4Cf3a71e
+ #25000000
+ #FFA500
+
+ #FFFFFF
+ #F8A500
+ #F8A500
+ #FFFFFF
+ @android:color/transparent
+ #99F8A500
+ #4CF8A500
+ #12FFFFFF
+ #C35817
\ No newline at end of file
diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml
index 9baada2..ef8403a 100644
--- a/demo/src/main/res/values/strings.xml
+++ b/demo/src/main/res/values/strings.xml
@@ -1,7 +1,7 @@
- StyledDialogs Demo
- Favourite Firefly quote
+ StyledDialogs Demo
+ Favourite Firefly quoteLove. Can know all the math in the \'verse but take a boat in the air that you
don\'t
love? She\'ll shake you off just as sure as a turn in the worlds. Love keeps her in the air when she oughtta
@@ -14,6 +14,6 @@
Mal: I\'m just waiting to see if I pass out. Long story."
- Love
- Hate
+ Love
+ Hate
diff --git a/demo/src/main/res/values/styles.xml b/demo/src/main/res/values/styles.xml
index e572623..23238d7 100644
--- a/demo/src/main/res/values/styles.xml
+++ b/demo/src/main/res/values/styles.xml
@@ -1,30 +1,30 @@
-
+
-
+
-
+
-
+
\ No newline at end of file
diff --git a/demo/src/main/res/values/theme.xml b/demo/src/main/res/values/theme.xml
index 5e76ecb..b17e436 100644
--- a/demo/src/main/res/values/theme.xml
+++ b/demo/src/main/res/values/theme.xml
@@ -1,54 +1,54 @@
-
+
-
+
-
+
-
+
-
+
-
+
\ No newline at end of file
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
index 7cf43ae..397ca8a 100644
--- a/library/src/main/AndroidManifest.xml
+++ b/library/src/main/AndroidManifest.xml
@@ -1,6 +1,4 @@
+ package="eu.inmite.android.lib.dialogs">
diff --git a/library/src/main/java/android/widget/numberpicker/backport/NumberPicker.java b/library/src/main/java/android/widget/numberpicker/backport/NumberPicker.java
new file mode 100644
index 0000000..a19d5de
--- /dev/null
+++ b/library/src/main/java/android/widget/numberpicker/backport/NumberPicker.java
@@ -0,0 +1,2662 @@
+/*
+ * 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 android.widget.numberpicker.backport;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.InputFilter;
+import android.text.InputType;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.method.NumberKeyListener;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.LayoutInflater.Filter;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.animation.DecelerateInterpolator;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import eu.inmite.android.lib.dialogs.R;
+
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+//import android.annotation.Widget;
+
+/**
+ * A widget that enables the user to select a number form a predefined range.
+ * There are two flavors of this widget and which one is presented to the user
+ * depends on the current theme.
+ *
+ *
+ * If the current theme is derived from {@link android.R.style#Theme} the widget
+ * presents the current value as an editable input field with an increment button
+ * above and a decrement button below. Long pressing the buttons allows for a quick
+ * change of the current value. Tapping on the input field allows to type in
+ * a desired value.
+ *
+ *
+ * If the current theme is derived from {@link android.R.style#Theme_Holo} or
+ * {@link android.R.style#Theme_Holo_Light} the widget presents the current
+ * value as an editable input field with a lesser value above and a greater
+ * value below. Tapping on the lesser or greater value selects it by animating
+ * the number axis up or down to make the chosen value current. Flinging up
+ * or down allows for multiple increments or decrements of the current value.
+ * Long pressing on the lesser and greater values also allows for a quick change
+ * of the current value. Tapping on the current value allows to type in a
+ * desired value.
+ *
+ *
+ *
+ * For an example of using this widget, see {@link android.widget.TimePicker}.
+ *
+ */
+//@Widget
+public class NumberPicker extends LinearLayout {
+
+ /**
+ * The number of items show in the selector wheel.
+ */
+ private static final int SELECTOR_WHEEL_ITEM_COUNT = 3;
+
+ /**
+ * The default update interval during long press.
+ */
+ private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
+
+ /**
+ * The index of the middle selector item.
+ */
+ private static final int SELECTOR_MIDDLE_ITEM_INDEX = SELECTOR_WHEEL_ITEM_COUNT / 2;
+
+ /**
+ * The coefficient by which to adjust (divide) the max fling velocity.
+ */
+ private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
+
+ /**
+ * The the duration for adjusting the selector wheel.
+ */
+ private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
+
+ /**
+ * The duration of scrolling while snapping to a given position.
+ */
+ private static final int SNAP_SCROLL_DURATION = 300;
+
+ /**
+ * The strength of fading in the top and bottom while drawing the selector.
+ */
+ private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
+
+ /**
+ * The default unscaled height of the selection divider.
+ */
+ private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
+
+ /**
+ * The default unscaled distance between the selection dividers.
+ */
+ private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48;
+
+ /**
+ * The resource id for the default layout.
+ */
+ private static final int DEFAULT_LAYOUT_RESOURCE_ID = 0;
+
+ /**
+ * Constant for unspecified size.
+ */
+ private static final int SIZE_UNSPECIFIED = -1;
+
+ /**
+ * Use a custom NumberPicker formatting callback to use two-digit minutes
+ * strings like "01". Keeping a static formatter etc. is the most efficient
+ * way to do this; it avoids creating temporary objects on every call to
+ * format().
+ */
+ private static class TwoDigitFormatter implements NumberPicker.Formatter {
+ final StringBuilder mBuilder = new StringBuilder();
+
+ char mZeroDigit;
+ java.util.Formatter mFmt;
+
+ final Object[] mArgs = new Object[1];
+
+ TwoDigitFormatter() {
+ final Locale locale = Locale.getDefault();
+ init(locale);
+ }
+
+ private void init(Locale locale) {
+ mFmt = createFormatter(locale);
+ mZeroDigit = getZeroDigit(locale);
+ }
+
+ public String format(int value) {
+ final Locale currentLocale = Locale.getDefault();
+ if (mZeroDigit != getZeroDigit(currentLocale)) {
+ init(currentLocale);
+ }
+ mArgs[0] = value;
+ mBuilder.delete(0, mBuilder.length());
+ mFmt.format("%02d", mArgs);
+ return mFmt.toString();
+ }
+
+ private static char getZeroDigit(Locale locale) {
+ // return LocaleData.get(locale).zeroDigit;
+ return new DecimalFormatSymbols(locale).getZeroDigit();
+ }
+
+ private java.util.Formatter createFormatter(Locale locale) {
+ return new java.util.Formatter(mBuilder, locale);
+ }
+ }
+
+ private static final TwoDigitFormatter sTwoDigitFormatter = new TwoDigitFormatter();
+
+ /**
+ * @hide
+ */
+ public static final Formatter getTwoDigitFormatter() {
+ return sTwoDigitFormatter;
+ }
+
+ /**
+ * The increment button.
+ */
+ private final ImageButton mIncrementButton;
+
+ /**
+ * The decrement button.
+ */
+ private final ImageButton mDecrementButton;
+
+ /**
+ * The text for showing the current value.
+ */
+ private final EditText mInputText;
+
+ /**
+ * The distance between the two selection dividers.
+ */
+ private final int mSelectionDividersDistance;
+
+ /**
+ * The min height of this widget.
+ */
+ private final int mMinHeight;
+
+ /**
+ * The max height of this widget.
+ */
+ private final int mMaxHeight;
+
+ /**
+ * The max width of this widget.
+ */
+ private final int mMinWidth;
+
+ /**
+ * The max width of this widget.
+ */
+ private int mMaxWidth;
+
+ /**
+ * Flag whether to compute the max width.
+ */
+ private final boolean mComputeMaxWidth;
+
+ /**
+ * The height of the text.
+ */
+ private final int mTextSize;
+
+ /**
+ * The height of the gap between text elements if the selector wheel.
+ */
+ private int mSelectorTextGapHeight;
+
+ /**
+ * The values to be displayed instead the indices.
+ */
+ private String[] mDisplayedValues;
+
+ /**
+ * Lower value of the range of numbers allowed for the NumberPicker
+ */
+ private int mMinValue;
+
+ /**
+ * Upper value of the range of numbers allowed for the NumberPicker
+ */
+ private int mMaxValue;
+
+ /**
+ * Current value of this NumberPicker
+ */
+ private int mValue;
+
+ /**
+ * Listener to be notified upon current value change.
+ */
+ private OnValueChangeListener mOnValueChangeListener;
+
+ /**
+ * Listener to be notified upon scroll state change.
+ */
+ private OnScrollListener mOnScrollListener;
+
+ /**
+ * Formatter for for displaying the current value.
+ */
+ private Formatter mFormatter;
+
+ /**
+ * The speed for updating the value form long press.
+ */
+ private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
+
+ /**
+ * Cache for the string representation of selector indices.
+ */
+ private final SparseArray mSelectorIndexToStringCache = new SparseArray();
+
+ /**
+ * The selector indices whose value are show by the selector.
+ */
+ private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT];
+
+ /**
+ * The {@link Paint} for drawing the selector.
+ */
+ private final Paint mSelectorWheelPaint;
+
+ /**
+ * The {@link Drawable} for pressed virtual (increment/decrement) buttons.
+ */
+ private final Drawable mVirtualButtonPressedDrawable;
+
+ /**
+ * The height of a selector element (text + gap).
+ */
+ private int mSelectorElementHeight;
+
+ /**
+ * The initial offset of the scroll selector.
+ */
+ private int mInitialScrollOffset = Integer.MIN_VALUE;
+
+ /**
+ * The current offset of the scroll selector.
+ */
+ private int mCurrentScrollOffset;
+
+ /**
+ * The {@link Scroller} responsible for flinging the selector.
+ */
+ private final Scroller mFlingScroller;
+
+ /**
+ * The {@link Scroller} responsible for adjusting the selector.
+ */
+ private final Scroller mAdjustScroller;
+
+ /**
+ * The previous Y coordinate while scrolling the selector.
+ */
+ private int mPreviousScrollerY;
+
+ /**
+ * Handle to the reusable command for setting the input text selection.
+ */
+ private SetSelectionCommand mSetSelectionCommand;
+
+ /**
+ * Handle to the reusable command for changing the current value from long
+ * press by one.
+ */
+ private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand;
+
+ /**
+ * Command for beginning an edit of the current value via IME on long press.
+ */
+ private BeginSoftInputOnLongPressCommand mBeginSoftInputOnLongPressCommand;
+
+ /**
+ * The Y position of the last down event.
+ */
+ private float mLastDownEventY;
+
+ /**
+ * The time of the last down event.
+ */
+ private long mLastDownEventTime;
+
+ /**
+ * The Y position of the last down or move event.
+ */
+ private float mLastDownOrMoveEventY;
+
+ /**
+ * Determines speed during touch scrolling.
+ */
+ private VelocityTracker mVelocityTracker;
+
+ /**
+ * @see ViewConfiguration#getScaledTouchSlop()
+ */
+ private int mTouchSlop;
+
+ /**
+ * @see ViewConfiguration#getScaledMinimumFlingVelocity()
+ */
+ private int mMinimumFlingVelocity;
+
+ /**
+ * @see ViewConfiguration#getScaledMaximumFlingVelocity()
+ */
+ private int mMaximumFlingVelocity;
+
+ /**
+ * Flag whether the selector should wrap around.
+ */
+ private boolean mWrapSelectorWheel;
+
+ /**
+ * The back ground color used to optimize scroller fading.
+ */
+ private final int mSolidColor;
+
+ /**
+ * Flag whether this widget has a selector wheel.
+ */
+ private final boolean mHasSelectorWheel;
+
+ /**
+ * Divider for showing item to be selected while scrolling
+ */
+ private final Drawable mSelectionDivider;
+
+ /**
+ * The height of the selection divider.
+ */
+ private final int mSelectionDividerHeight;
+
+ /**
+ * The current scroll state of the number picker.
+ */
+ private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * Flag whether to ignore move events - we ignore such when we show in IME
+ * to prevent the content from scrolling.
+ */
+ private boolean mIngonreMoveEvents;
+
+ /**
+ * Flag whether to show soft input on tap.
+ */
+ private boolean mShowSoftInputOnTap;
+
+ /**
+ * The top of the top selection divider.
+ */
+ private int mTopSelectionDividerTop;
+
+ /**
+ * The bottom of the bottom selection divider.
+ */
+ private int mBottomSelectionDividerBottom;
+
+ /**
+ * The virtual id of the last hovered child.
+ */
+ private int mLastHoveredChildVirtualViewId;
+
+ /**
+ * Whether the increment virtual button is pressed.
+ */
+ private boolean mIncrementVirtualButtonPressed;
+
+ /**
+ * Whether the decrement virtual button is pressed.
+ */
+ private boolean mDecrementVirtualButtonPressed;
+
+ /**
+ * Provider to report to clients the semantic structure of this widget.
+ */
+ private SupportAccessibilityNodeProvider mAccessibilityNodeProvider;
+
+ /**
+ * Helper class for managing pressed state of the virtual buttons.
+ */
+ private final PressedStateHelper mPressedStateHelper;
+
+ /**
+ * The keycode of the last handled DPAD down event.
+ */
+ private int mLastHandledDownDpadKeyCode = -1;
+
+ /**
+ * Interface to listen for changes of the current value.
+ */
+ public interface OnValueChangeListener {
+
+ /**
+ * Called upon a change of the current value.
+ *
+ * @param picker The NumberPicker associated with this listener.
+ * @param oldVal The previous value.
+ * @param newVal The new value.
+ */
+ void onValueChange(NumberPicker picker, int oldVal, int newVal);
+ }
+
+ /**
+ * Interface to listen for the picker scroll state.
+ */
+ public interface OnScrollListener {
+
+ /**
+ * The view is not scrolling.
+ */
+ public static int SCROLL_STATE_IDLE = 0;
+
+ /**
+ * The user is scrolling using touch, and his finger is still on the screen.
+ */
+ public static int SCROLL_STATE_TOUCH_SCROLL = 1;
+
+ /**
+ * The user had previously been scrolling using touch and performed a fling.
+ */
+ public static int SCROLL_STATE_FLING = 2;
+
+ /**
+ * Callback invoked while the number picker scroll state has changed.
+ *
+ * @param view The view whose scroll state is being reported.
+ * @param scrollState The current scroll state. One of
+ * {@link #SCROLL_STATE_IDLE},
+ * {@link #SCROLL_STATE_TOUCH_SCROLL} or
+ * {@link #SCROLL_STATE_IDLE}.
+ */
+ public void onScrollStateChange(NumberPicker view, int scrollState);
+ }
+
+ /**
+ * Interface used to format current value into a string for presentation.
+ */
+ public interface Formatter {
+
+ /**
+ * Formats a string representation of the current value.
+ *
+ * @param value The currently selected value.
+ * @return A formatted string representation.
+ */
+ public String format(int value);
+ }
+
+ /**
+ * Create a new number picker.
+ *
+ * @param context The application environment.
+ */
+ public NumberPicker(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Create a new number picker.
+ *
+ * @param context The application environment.
+ * @param attrs A collection of attributes.
+ */
+ public NumberPicker(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.numberPickerStyle);
+ }
+
+ /**
+ * Create a new number picker
+ *
+ * @param context the application environment.
+ * @param attrs a collection of attributes.
+ * @param defStyle The default style to apply to this view.
+ */
+ public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+
+ // process style attributes
+ TypedArray attributesArray = context.obtainStyledAttributes(
+ attrs, R.styleable.NumberPicker, defStyle, 0);
+ final int layoutResId = attributesArray.getResourceId(
+ R.styleable.NumberPicker_internalLayout, DEFAULT_LAYOUT_RESOURCE_ID);
+
+ mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
+
+ mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
+
+ mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
+
+ final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
+ getResources().getDisplayMetrics());
+ mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
+
+ final int defSelectionDividerDistance = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE,
+ getResources().getDisplayMetrics());
+ mSelectionDividersDistance = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance);
+
+ mMinHeight = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
+
+ mMaxHeight = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_internalMaxHeight, SIZE_UNSPECIFIED);
+ if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
+ && mMinHeight > mMaxHeight) {
+ throw new IllegalArgumentException("minHeight > maxHeight");
+ }
+
+ mMinWidth = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_internalMinWidth, SIZE_UNSPECIFIED);
+
+ mMaxWidth = attributesArray.getDimensionPixelSize(
+ R.styleable.NumberPicker_internalMaxWidth, SIZE_UNSPECIFIED);
+ if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
+ && mMinWidth > mMaxWidth) {
+ throw new IllegalArgumentException("minWidth > maxWidth");
+ }
+
+ mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED);
+
+ mVirtualButtonPressedDrawable = attributesArray.getDrawable(
+ R.styleable.NumberPicker_virtualButtonPressedDrawable);
+
+ attributesArray.recycle();
+
+ mPressedStateHelper = new PressedStateHelper();
+
+ // By default Linearlayout that we extend is not drawn. This is
+ // its draw() method is not called but dispatchDraw() is called
+ // directly (see ViewGroup.drawChild()). However, this class uses
+ // the fading edge effect implemented by View and we need our
+ // draw() method to be called. Therefore, we declare we will draw.
+ setWillNotDraw(!mHasSelectorWheel);
+
+ LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(layoutResId, this, true);
+
+ OnClickListener onClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ hideSoftInput();
+ mInputText.clearFocus();
+ if (v.getId() == R.id.sdl__np__increment) {
+ changeValueByOne(true);
+ } else {
+ changeValueByOne(false);
+ }
+ }
+ };
+
+ OnLongClickListener onLongClickListener = new OnLongClickListener() {
+ public boolean onLongClick(View v) {
+ hideSoftInput();
+ mInputText.clearFocus();
+ if (v.getId() == R.id.sdl__np__increment) {
+ postChangeCurrentByOneFromLongPress(true, 0);
+ } else {
+ postChangeCurrentByOneFromLongPress(false, 0);
+ }
+ return true;
+ }
+ };
+
+ // increment button
+ if (!mHasSelectorWheel) {
+ mIncrementButton = (ImageButton) findViewById(R.id.sdl__np__increment);
+ mIncrementButton.setOnClickListener(onClickListener);
+ mIncrementButton.setOnLongClickListener(onLongClickListener);
+ } else {
+ mIncrementButton = null;
+ }
+
+ // decrement button
+ if (!mHasSelectorWheel) {
+ mDecrementButton = (ImageButton) findViewById(R.id.sdl__np__decrement);
+ mDecrementButton.setOnClickListener(onClickListener);
+ mDecrementButton.setOnLongClickListener(onLongClickListener);
+ } else {
+ mDecrementButton = null;
+ }
+
+ // input text
+ mInputText = (EditText) findViewById(R.id.np__numberpicker_input);
+ mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ mInputText.selectAll();
+ } else {
+ mInputText.setSelection(0, 0);
+ validateInputTextView(v);
+ }
+ }
+ });
+ mInputText.setFilters(new InputFilter[] {
+ new InputTextFilter()
+ });
+
+ mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
+ mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
+
+ // initialize constants
+ ViewConfiguration configuration = ViewConfiguration.get(context);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
+ / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
+ mTextSize = (int) mInputText.getTextSize();
+
+ // create the selector wheel paint
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setTextAlign(Align.CENTER);
+ paint.setTextSize(mTextSize);
+ paint.setTypeface(mInputText.getTypeface());
+ ColorStateList colors = mInputText.getTextColors();
+ int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
+ paint.setColor(color);
+ mSelectorWheelPaint = paint;
+
+ // create the fling and adjust scrollers
+ mFlingScroller = new Scroller(getContext(), null, true);
+ mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
+
+ updateInputTextView();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ // If not explicitly specified this view is important for accessibility.
+ if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mHasSelectorWheel) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+ final int msrdWdth = getMeasuredWidth();
+ final int msrdHght = getMeasuredHeight();
+
+ // Input text centered horizontally.
+ final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
+ final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
+ final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
+ final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
+ final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
+ final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
+ mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
+
+ if (changed) {
+ // need to do all this when we know our size
+ initializeSelectorWheel();
+ initializeFadingEdges();
+ mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
+ - mSelectionDividerHeight;
+ mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
+ + mSelectionDividersDistance;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (!mHasSelectorWheel) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+ // Try greedily to fit the max width and height.
+ final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
+ final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
+ super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
+ // Flag if we are measured with width or height less than the respective min.
+ final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(),
+ widthMeasureSpec);
+ final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(),
+ heightMeasureSpec);
+ setMeasuredDimension(widthSize, heightSize);
+ }
+
+ /**
+ * Move to the final position of a scroller. Ensures to force finish the scroller
+ * and if it is not at its final position a scroll of the selector wheel is
+ * performed to fast forward to the final position.
+ *
+ * @param scroller The scroller to whose final position to get.
+ * @return True of the a move was performed, i.e. the scroller was not in final position.
+ */
+ private boolean moveToFinalScrollerPosition(Scroller scroller) {
+ scroller.forceFinished(true);
+ int amountToScroll = scroller.getFinalY() - scroller.getCurrY();
+ int futureScrollOffset = (mCurrentScrollOffset + amountToScroll) % mSelectorElementHeight;
+ int overshootAdjustment = mInitialScrollOffset - futureScrollOffset;
+ if (overshootAdjustment != 0) {
+ if (Math.abs(overshootAdjustment) > mSelectorElementHeight / 2) {
+ if (overshootAdjustment > 0) {
+ overshootAdjustment -= mSelectorElementHeight;
+ } else {
+ overshootAdjustment += mSelectorElementHeight;
+ }
+ }
+ amountToScroll += overshootAdjustment;
+ scrollBy(0, amountToScroll);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (!mHasSelectorWheel || !isEnabled()) {
+ return false;
+ }
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ removeAllCallbacks();
+ mInputText.setVisibility(View.INVISIBLE);
+ mLastDownOrMoveEventY = mLastDownEventY = event.getY();
+ mLastDownEventTime = event.getEventTime();
+ mIngonreMoveEvents = false;
+ mShowSoftInputOnTap = false;
+ // Handle pressed state before any state change.
+ if (mLastDownEventY < mTopSelectionDividerTop) {
+ if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
+ mPressedStateHelper.buttonPressDelayed(
+ PressedStateHelper.BUTTON_DECREMENT);
+ }
+ } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
+ if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
+ mPressedStateHelper.buttonPressDelayed(
+ PressedStateHelper.BUTTON_INCREMENT);
+ }
+ }
+ // Make sure we support flinging inside scrollables.
+ getParent().requestDisallowInterceptTouchEvent(true);
+ if (!mFlingScroller.isFinished()) {
+ mFlingScroller.forceFinished(true);
+ mAdjustScroller.forceFinished(true);
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ } else if (!mAdjustScroller.isFinished()) {
+ mFlingScroller.forceFinished(true);
+ mAdjustScroller.forceFinished(true);
+ } else if (mLastDownEventY < mTopSelectionDividerTop) {
+ hideSoftInput();
+ postChangeCurrentByOneFromLongPress(
+ false, ViewConfiguration.getLongPressTimeout());
+ } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
+ hideSoftInput();
+ postChangeCurrentByOneFromLongPress(
+ true, ViewConfiguration.getLongPressTimeout());
+ } else {
+ mShowSoftInputOnTap = true;
+ postBeginSoftInputOnLongPressCommand();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled() || !mHasSelectorWheel) {
+ return false;
+ }
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(event);
+ int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_MOVE: {
+ if (mIngonreMoveEvents) {
+ break;
+ }
+ float currentMoveY = event.getY();
+ if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
+ if (deltaDownY > mTouchSlop) {
+ removeAllCallbacks();
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ }
+ } else {
+ int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY));
+ scrollBy(0, deltaMoveY);
+ invalidate();
+ }
+ mLastDownOrMoveEventY = currentMoveY;
+ } break;
+ case MotionEvent.ACTION_UP: {
+ removeBeginSoftInputCommand();
+ removeChangeCurrentByOneFromLongPress();
+ mPressedStateHelper.cancel();
+ VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
+ int initialVelocity = (int) velocityTracker.getYVelocity();
+ if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
+ fling(initialVelocity);
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ } else {
+ int eventY = (int) event.getY();
+ int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY);
+ long deltaTime = event.getEventTime() - mLastDownEventTime;
+ long tapTimeout = ViewConfiguration.getTapTimeout();
+ if (deltaMoveY <= mTouchSlop) { // && deltaTime < ViewConfiguration.getTapTimeout()) {
+ if (mShowSoftInputOnTap) {
+ mShowSoftInputOnTap = false;
+ showSoftInput();
+ } else {
+ int selectorIndexOffset = (eventY / mSelectorElementHeight)
+ - SELECTOR_MIDDLE_ITEM_INDEX;
+ if (selectorIndexOffset > 0) {
+ changeValueByOne(true);
+ mPressedStateHelper.buttonTapped(
+ PressedStateHelper.BUTTON_INCREMENT);
+ } else if (selectorIndexOffset < 0) {
+ changeValueByOne(false);
+ mPressedStateHelper.buttonTapped(
+ PressedStateHelper.BUTTON_DECREMENT);
+ }
+ }
+ } else {
+ ensureScrollWheelAdjusted();
+ }
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ } break;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ removeAllCallbacks();
+ break;
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ removeAllCallbacks();
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (!mHasSelectorWheel) {
+ break;
+ }
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+ ? getValue() < getMaxValue() : getValue() > getMinValue()) {
+ requestFocus();
+ mLastHandledDownDpadKeyCode = keyCode;
+ removeAllCallbacks();
+ if (mFlingScroller.isFinished()) {
+ changeValueByOne(keyCode == KeyEvent.KEYCODE_DPAD_DOWN);
+ }
+ return true;
+ }
+ break;
+ case KeyEvent.ACTION_UP:
+ if (mLastHandledDownDpadKeyCode == keyCode) {
+ mLastHandledDownDpadKeyCode = -1;
+ return true;
+ }
+ break;
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent event) {
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ removeAllCallbacks();
+ break;
+ }
+ return super.dispatchTrackballEvent(event);
+ }
+
+ @Override
+ protected boolean dispatchHoverEvent(MotionEvent event) {
+ if (!mHasSelectorWheel) {
+ return super.dispatchHoverEvent(event);
+ }
+
+ if (((AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE)).isEnabled()) {
+ final int eventY = (int) event.getY();
+ final int hoveredVirtualViewId;
+ if (eventY < mTopSelectionDividerTop) {
+ hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_DECREMENT;
+ } else if (eventY > mBottomSelectionDividerBottom) {
+ hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INCREMENT;
+ } else {
+ hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INPUT;
+ }
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ SupportAccessibilityNodeProvider provider = getSupportAccessibilityNodeProvider();
+
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER: {
+ provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+ provider.performAction(hoveredVirtualViewId,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ } break;
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
+ && mLastHoveredChildVirtualViewId != View.NO_ID) {
+ provider.sendAccessibilityEventForVirtualView(
+ mLastHoveredChildVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+ provider.performAction(hoveredVirtualViewId,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ }
+ } break;
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
+ AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ mLastHoveredChildVirtualViewId = View.NO_ID;
+ } break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void computeScroll() {
+ Scroller scroller = mFlingScroller;
+ if (scroller.isFinished()) {
+ scroller = mAdjustScroller;
+ if (scroller.isFinished()) {
+ return;
+ }
+ }
+ scroller.computeScrollOffset();
+ int currentScrollerY = scroller.getCurrY();
+ if (mPreviousScrollerY == 0) {
+ mPreviousScrollerY = scroller.getStartY();
+ }
+ scrollBy(0, currentScrollerY - mPreviousScrollerY);
+ mPreviousScrollerY = currentScrollerY;
+ if (scroller.isFinished()) {
+ onScrollerFinished(scroller);
+ } else {
+ invalidate();
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (!mHasSelectorWheel) {
+ mIncrementButton.setEnabled(enabled);
+ }
+ if (!mHasSelectorWheel) {
+ mDecrementButton.setEnabled(enabled);
+ }
+ mInputText.setEnabled(enabled);
+ }
+
+ @Override
+ public void scrollBy(int x, int y) {
+ int[] selectorIndices = mSelectorIndices;
+ if (!mWrapSelectorWheel && y > 0
+ && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
+ mCurrentScrollOffset = mInitialScrollOffset;
+ return;
+ }
+ if (!mWrapSelectorWheel && y < 0
+ && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
+ mCurrentScrollOffset = mInitialScrollOffset;
+ return;
+ }
+ mCurrentScrollOffset += y;
+ while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) {
+ mCurrentScrollOffset -= mSelectorElementHeight;
+ decrementSelectorIndices(selectorIndices);
+ setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
+ if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
+ mCurrentScrollOffset = mInitialScrollOffset;
+ }
+ }
+ while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) {
+ mCurrentScrollOffset += mSelectorElementHeight;
+ incrementSelectorIndices(selectorIndices);
+ setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
+ if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
+ mCurrentScrollOffset = mInitialScrollOffset;
+ }
+ }
+ }
+
+ @Override
+ public int getSolidColor() {
+ return mSolidColor;
+ }
+
+ /**
+ * Sets the listener to be notified on change of the current value.
+ *
+ * @param onValueChangedListener The listener.
+ */
+ public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
+ mOnValueChangeListener = onValueChangedListener;
+ }
+
+ /**
+ * Set listener to be notified for scroll state changes.
+ *
+ * @param onScrollListener The listener.
+ */
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListener = onScrollListener;
+ }
+
+ /**
+ * Set the formatter to be used for formatting the current value.
+ *
+ * Note: If you have provided alternative values for the values this
+ * formatter is never invoked.
+ *
+ *
+ * @param formatter The formatter object. If formatter is null,
+ * {@link String#valueOf(int)} will be used.
+ *@see #setDisplayedValues(String[])
+ */
+ public void setFormatter(Formatter formatter) {
+ if (formatter == mFormatter) {
+ return;
+ }
+ mFormatter = formatter;
+ initializeSelectorWheelIndices();
+ updateInputTextView();
+ }
+
+ /**
+ * Set the current value for the number picker.
+ *
+ * If the argument is less than the {@link NumberPicker#getMinValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is false the
+ * current value is set to the {@link NumberPicker#getMinValue()} value.
+ *
+ *
+ * If the argument is less than the {@link NumberPicker#getMinValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is true the
+ * current value is set to the {@link NumberPicker#getMaxValue()} value.
+ *
+ *
+ * If the argument is less than the {@link NumberPicker#getMaxValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is false the
+ * current value is set to the {@link NumberPicker#getMaxValue()} value.
+ *
+ *
+ * If the argument is less than the {@link NumberPicker#getMaxValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is true the
+ * current value is set to the {@link NumberPicker#getMinValue()} value.
+ *
+ *
+ * @param value The current value.
+ * @see #setWrapSelectorWheel(boolean)
+ * @see #setMinValue(int)
+ * @see #setMaxValue(int)
+ */
+ public void setValue(int value) {
+ setValueInternal(value, false);
+ }
+
+ /**
+ * Shows the soft input for its input text.
+ */
+ private void showSoftInput() {
+ InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (inputMethodManager != null) {
+ if (mHasSelectorWheel) {
+ mInputText.setVisibility(View.VISIBLE);
+ }
+ mInputText.requestFocus();
+ inputMethodManager.showSoftInput(mInputText, 0);
+ }
+ }
+
+ /**
+ * Hides the soft input if it is active for the input text.
+ */
+ private void hideSoftInput() {
+ InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
+ inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ if (mHasSelectorWheel) {
+ mInputText.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ /**
+ * Computes the max width if no such specified as an attribute.
+ */
+ private void tryComputeMaxWidth() {
+// if (!mComputeMaxWidth) {
+// return;
+// }
+// int maxTextWidth = 0;
+// if (mDisplayedValues == null) {
+// float maxDigitWidth = 0;
+// for (int i = 0; i <= 9; i++) {
+// final float digitWidth = mSelectorWheelPaint.measureText(formatNumberWithLocale(i));
+// if (digitWidth > maxDigitWidth) {
+// maxDigitWidth = digitWidth;
+// }
+// }
+// int numberOfDigits = 0;
+// int current = mMaxValue;
+// while (current > 0) {
+// numberOfDigits++;
+// current = current / 10;
+// }
+// maxTextWidth = (int) (numberOfDigits * maxDigitWidth);
+// } else {
+// final int valueCount = mDisplayedValues.length;
+// for (int i = 0; i < valueCount; i++) {
+// final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValues[i]);
+// if (textWidth > maxTextWidth) {
+// maxTextWidth = (int) textWidth;
+// }
+// }
+// }
+// maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight();
+// if (mMaxWidth != maxTextWidth) {
+// if (maxTextWidth > mMinWidth) {
+// mMaxWidth = maxTextWidth;
+// } else {
+// mMaxWidth = mMinWidth;
+// }
+// invalidate();
+// }
+ }
+
+ /**
+ * Gets whether the selector wheel wraps when reaching the min/max value.
+ *
+ * @return True if the selector wheel wraps.
+ *
+ * @see #getMinValue()
+ * @see #getMaxValue()
+ */
+ public boolean getWrapSelectorWheel() {
+ return mWrapSelectorWheel;
+ }
+
+ /**
+ * Sets whether the selector wheel shown during flinging/scrolling should
+ * wrap around the {@link NumberPicker#getMinValue()} and
+ * {@link NumberPicker#getMaxValue()} values.
+ *
+ * By default if the range (max - min) is more than the number of items shown
+ * on the selector wheel the selector wheel wrapping is enabled.
+ *
+ *
+ * Note: If the number of items, i.e. the range (
+ * {@link #getMaxValue()} - {@link #getMinValue()}) is less than
+ * the number of items shown on the selector wheel, the selector wheel will
+ * not wrap. Hence, in such a case calling this method is a NOP.
+ *
+ *
+ * @param wrapSelectorWheel Whether to wrap.
+ */
+ public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
+ final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length;
+ if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) {
+ mWrapSelectorWheel = wrapSelectorWheel;
+ }
+ }
+
+ /**
+ * Sets the speed at which the numbers be incremented and decremented when
+ * the up and down buttons are long pressed respectively.
+ *
+ * The default value is 300 ms.
+ *
+ *
+ * @param intervalMillis The speed (in milliseconds) at which the numbers
+ * will be incremented and decremented.
+ */
+ public void setOnLongPressUpdateInterval(long intervalMillis) {
+ mLongPressUpdateInterval = intervalMillis;
+ }
+
+ /**
+ * Returns the value of the picker.
+ *
+ * @return The value.
+ */
+ public int getValue() {
+ return mValue;
+ }
+
+ /**
+ * Returns the min value of the picker.
+ *
+ * @return The min value
+ */
+ public int getMinValue() {
+ return mMinValue;
+ }
+
+ /**
+ * Sets the min value of the picker.
+ *
+ * @param minValue The min value inclusive.
+ *
+ * Note: The length of the displayed values array
+ * set via {@link #setDisplayedValues(String[])} must be equal to the
+ * range of selectable numbers which is equal to
+ * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
+ */
+ public void setMinValue(int minValue) {
+ if (mMinValue == minValue) {
+ return;
+ }
+ if (minValue < 0) {
+ throw new IllegalArgumentException("minValue must be >= 0");
+ }
+ mMinValue = minValue;
+ if (mMinValue > mValue) {
+ mValue = mMinValue;
+ }
+ boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
+ setWrapSelectorWheel(wrapSelectorWheel);
+ initializeSelectorWheelIndices();
+ updateInputTextView();
+ tryComputeMaxWidth();
+ invalidate();
+ }
+
+ /**
+ * Returns the max value of the picker.
+ *
+ * @return The max value.
+ */
+ public int getMaxValue() {
+ return mMaxValue;
+ }
+
+ /**
+ * Sets the max value of the picker.
+ *
+ * @param maxValue The max value inclusive.
+ *
+ * Note: The length of the displayed values array
+ * set via {@link #setDisplayedValues(String[])} must be equal to the
+ * range of selectable numbers which is equal to
+ * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
+ */
+ public void setMaxValue(int maxValue) {
+ if (mMaxValue == maxValue) {
+ return;
+ }
+ if (maxValue < 0) {
+ throw new IllegalArgumentException("maxValue must be >= 0");
+ }
+ mMaxValue = maxValue;
+ if (mMaxValue < mValue) {
+ mValue = mMaxValue;
+ }
+ boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
+ setWrapSelectorWheel(wrapSelectorWheel);
+ initializeSelectorWheelIndices();
+ updateInputTextView();
+ tryComputeMaxWidth();
+ invalidate();
+ }
+
+ /**
+ * Gets the values to be displayed instead of string values.
+ *
+ * @return The displayed values.
+ */
+ public String[] getDisplayedValues() {
+ return mDisplayedValues;
+ }
+
+ /**
+ * Sets the values to be displayed.
+ *
+ * @param displayedValues The displayed values.
+ *
+ * Note: The length of the displayed values array
+ * must be equal to the range of selectable numbers which is equal to
+ * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
+ */
+ public void setDisplayedValues(String[] displayedValues) {
+ if (mDisplayedValues == displayedValues) {
+ return;
+ }
+ mDisplayedValues = displayedValues;
+ if (mDisplayedValues != null) {
+ // Allow text entry rather than strictly numeric entry.
+ mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+ } else {
+ mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
+ }
+ updateInputTextView();
+ initializeSelectorWheelIndices();
+ tryComputeMaxWidth();
+ }
+
+ @Override
+ protected float getTopFadingEdgeStrength() {
+ return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
+ }
+
+ @Override
+ protected float getBottomFadingEdgeStrength() {
+ return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeAllCallbacks();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (!mHasSelectorWheel) {
+ super.onDraw(canvas);
+ return;
+ }
+ float x = (getRight() - getLeft()) / 2;
+ float y = mCurrentScrollOffset;
+
+ // draw the virtual buttons pressed state if needed
+ if (mVirtualButtonPressedDrawable != null
+ && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
+ if (mDecrementVirtualButtonPressed) {
+ //mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
+ mVirtualButtonPressedDrawable.setState(PRESSED_ENABLED_STATE_SET);
+ mVirtualButtonPressedDrawable.setBounds(0, 0, getRight(), mTopSelectionDividerTop);
+ mVirtualButtonPressedDrawable.draw(canvas);
+ }
+ if (mIncrementVirtualButtonPressed) {
+ //mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
+ mVirtualButtonPressedDrawable.setState(PRESSED_ENABLED_STATE_SET);
+ mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, getRight(),
+ getBottom());
+ mVirtualButtonPressedDrawable.draw(canvas);
+ }
+ }
+
+ // draw the selector wheel
+ int[] selectorIndices = mSelectorIndices;
+ for (int i = 0; i < selectorIndices.length; i++) {
+ int selectorIndex = selectorIndices[i];
+ String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
+ // Do not draw the middle item if input is visible since the input
+ // is shown only if the wheel is static and it covers the middle
+ // item. Otherwise, if the user starts editing the text via the
+ // IME he may see a dimmed version of the old value intermixed
+ // with the new one.
+ if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
+ canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
+ }
+ y += mSelectorElementHeight;
+ }
+
+ // draw the selection dividers
+ if (mSelectionDivider != null) {
+ // draw the top divider
+ int topOfTopDivider = mTopSelectionDividerTop;
+ int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
+ mSelectionDivider.setBounds(0, topOfTopDivider, getRight(), bottomOfTopDivider);
+ mSelectionDivider.draw(canvas);
+
+ // draw the bottom divider
+ int bottomOfBottomDivider = mBottomSelectionDividerBottom;
+ int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
+ mSelectionDivider.setBounds(0, topOfBottomDivider, getRight(), bottomOfBottomDivider);
+ mSelectionDivider.draw(canvas);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(NumberPicker.class.getName());
+ event.setScrollable(true);
+ event.setScrollY((mMinValue + mValue) * mSelectorElementHeight);
+ event.setMaxScrollY((mMaxValue - mMinValue) * mSelectorElementHeight);
+ }
+
+ @Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ if (!mHasSelectorWheel) {
+ return super.getAccessibilityNodeProvider();
+ }
+ if (mAccessibilityNodeProvider == null) {
+ mAccessibilityNodeProvider = new SupportAccessibilityNodeProvider();
+ }
+ return mAccessibilityNodeProvider.mProvider;
+ }
+
+ /**
+ * Makes a measure spec that tries greedily to use the max value.
+ *
+ * @param measureSpec The measure spec.
+ * @param maxSize The max value for the size.
+ * @return A measure spec greedily imposing the max size.
+ */
+ private int makeMeasureSpec(int measureSpec, int maxSize) {
+ if (maxSize == SIZE_UNSPECIFIED) {
+ return measureSpec;
+ }
+ final int size = MeasureSpec.getSize(measureSpec);
+ final int mode = MeasureSpec.getMode(measureSpec);
+ switch (mode) {
+ case MeasureSpec.EXACTLY:
+ return measureSpec;
+ case MeasureSpec.AT_MOST:
+ return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY);
+ case MeasureSpec.UNSPECIFIED:
+ return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY);
+ default:
+ throw new IllegalArgumentException("Unknown measure mode: " + mode);
+ }
+ }
+
+ /**
+ * Utility to reconcile a desired size and state, with constraints imposed
+ * by a MeasureSpec. Tries to respect the min size, unless a different size
+ * is imposed by the constraints.
+ *
+ * @param minSize The minimal desired size.
+ * @param measuredSize The currently measured size.
+ * @param measureSpec The current measure spec.
+ * @return The resolved size and state.
+ */
+ private int resolveSizeAndStateRespectingMinSize(
+ int minSize, int measuredSize, int measureSpec) {
+ if (minSize != SIZE_UNSPECIFIED) {
+ final int desiredWidth = Math.max(minSize, measuredSize);
+ return resolveSizeAndState(desiredWidth, measureSpec, 0);
+ } else {
+ return measuredSize;
+ }
+ }
+
+ /**
+ * Utility to reconcile a desired size and state, with constraints imposed
+ * by a MeasureSpec. Will take the desired size, unless a different size
+ * is imposed by the constraints. The returned value is a compound integer,
+ * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
+ * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
+ * size is smaller than the size the view wants to be.
+ *
+ * @param size How big the view wants to be
+ * @param measureSpec Constraints imposed by the parent
+ * @return Size information bit mask as defined by
+ * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
+ */
+ public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
+ int result = size;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+ switch (specMode) {
+ case MeasureSpec.UNSPECIFIED:
+ result = size;
+ break;
+ case MeasureSpec.AT_MOST:
+ if (specSize < size) {
+ result = specSize | MEASURED_STATE_TOO_SMALL;
+ } else {
+ result = size;
+ }
+ break;
+ case MeasureSpec.EXACTLY:
+ result = specSize;
+ break;
+ }
+ return result | (childMeasuredState&MEASURED_STATE_MASK);
+ }
+
+ /**
+ * Resets the selector indices and clear the cached string representation of
+ * these indices.
+ */
+ private void initializeSelectorWheelIndices() {
+ mSelectorIndexToStringCache.clear();
+ int[] selectorIndices = mSelectorIndices;
+ int current = getValue();
+ for (int i = 0; i < mSelectorIndices.length; i++) {
+ int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
+ if (mWrapSelectorWheel) {
+ selectorIndex = getWrappedSelectorIndex(selectorIndex);
+ }
+ selectorIndices[i] = selectorIndex;
+ ensureCachedScrollSelectorValue(selectorIndices[i]);
+ }
+ }
+
+ /**
+ * Sets the current value of this NumberPicker.
+ *
+ * @param current The new value of the NumberPicker.
+ * @param notifyChange Whether to notify if the current value changed.
+ */
+ private void setValueInternal(int current, boolean notifyChange) {
+ if (mValue == current) {
+ return;
+ }
+ // Wrap around the values if we go past the start or end
+ if (mWrapSelectorWheel) {
+ current = getWrappedSelectorIndex(current);
+ } else {
+ current = Math.max(current, mMinValue);
+ current = Math.min(current, mMaxValue);
+ }
+ int previous = mValue;
+ mValue = current;
+ updateInputTextView();
+ if (notifyChange) {
+ notifyChange(previous, current);
+ }
+ initializeSelectorWheelIndices();
+ invalidate();
+ }
+
+ /**
+ * Changes the current value by one which is increment or
+ * decrement based on the passes argument.
+ * decrement the current value.
+ *
+ * @param increment True to increment, false to decrement.
+ */
+ private void changeValueByOne(boolean increment) {
+ if (mHasSelectorWheel) {
+ mInputText.setVisibility(View.INVISIBLE);
+ if (!moveToFinalScrollerPosition(mFlingScroller)) {
+ moveToFinalScrollerPosition(mAdjustScroller);
+ }
+ mPreviousScrollerY = 0;
+ if (increment) {
+ mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight, SNAP_SCROLL_DURATION);
+ } else {
+ mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, SNAP_SCROLL_DURATION);
+ }
+ invalidate();
+ } else {
+ if (increment) {
+ setValueInternal(mValue + 1, true);
+ } else {
+ setValueInternal(mValue - 1, true);
+ }
+ }
+ }
+
+ private void initializeSelectorWheel() {
+ initializeSelectorWheelIndices();
+ int[] selectorIndices = mSelectorIndices;
+ int totalTextHeight = selectorIndices.length * mTextSize;
+ float totalTextGapHeight = (getBottom() - getTop()) - totalTextHeight;
+ float textGapCount = selectorIndices.length;
+ mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
+ mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
+ // Ensure that the middle item is positioned the same as the text in
+ // mInputText
+ int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
+ mInitialScrollOffset = editTextTextPosition
+ - (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
+ mCurrentScrollOffset = mInitialScrollOffset;
+ updateInputTextView();
+ }
+
+ private void initializeFadingEdges() {
+ setVerticalFadingEdgeEnabled(true);
+ setFadingEdgeLength((getBottom() - getTop() - mTextSize) / 2);
+ }
+
+ /**
+ * Callback invoked upon completion of a given scroller.
+ */
+ private void onScrollerFinished(Scroller scroller) {
+ if (scroller == mFlingScroller) {
+ if (!ensureScrollWheelAdjusted()) {
+ updateInputTextView();
+ }
+ onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ } else {
+ if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ updateInputTextView();
+ }
+ }
+ }
+
+ /**
+ * Handles transition to a given scrollState
+ */
+ private void onScrollStateChange(int scrollState) {
+ if (mScrollState == scrollState) {
+ return;
+ }
+ mScrollState = scrollState;
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollStateChange(this, scrollState);
+ }
+ }
+
+ /**
+ * Flings the selector with the given velocityY.
+ */
+ private void fling(int velocityY) {
+ mPreviousScrollerY = 0;
+
+ if (velocityY > 0) {
+ mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
+ } else {
+ mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
+ }
+
+ invalidate();
+ }
+
+ /**
+ * @return The wrapped index selectorIndex value.
+ */
+ private int getWrappedSelectorIndex(int selectorIndex) {
+ if (selectorIndex > mMaxValue) {
+ return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
+ } else if (selectorIndex < mMinValue) {
+ return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
+ }
+ return selectorIndex;
+ }
+
+ /**
+ * Increments the selectorIndices whose string representations
+ * will be displayed in the selector.
+ */
+ private void incrementSelectorIndices(int[] selectorIndices) {
+ for (int i = 0; i < selectorIndices.length - 1; i++) {
+ selectorIndices[i] = selectorIndices[i + 1];
+ }
+ int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
+ if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
+ nextScrollSelectorIndex = mMinValue;
+ }
+ selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
+ ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
+ }
+
+ /**
+ * Decrements the selectorIndices whose string representations
+ * will be displayed in the selector.
+ */
+ private void decrementSelectorIndices(int[] selectorIndices) {
+ for (int i = selectorIndices.length - 1; i > 0; i--) {
+ selectorIndices[i] = selectorIndices[i - 1];
+ }
+ int nextScrollSelectorIndex = selectorIndices[1] - 1;
+ if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
+ nextScrollSelectorIndex = mMaxValue;
+ }
+ selectorIndices[0] = nextScrollSelectorIndex;
+ ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
+ }
+
+ /**
+ * Ensures we have a cached string representation of the given
+ * selectorIndex to avoid multiple instantiations of the same string.
+ */
+ private void ensureCachedScrollSelectorValue(int selectorIndex) {
+ SparseArray cache = mSelectorIndexToStringCache;
+ String scrollSelectorValue = cache.get(selectorIndex);
+ if (scrollSelectorValue != null) {
+ return;
+ }
+ if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
+ scrollSelectorValue = "";
+ } else {
+ if (mDisplayedValues != null) {
+ int displayedValueIndex = selectorIndex - mMinValue;
+ scrollSelectorValue = mDisplayedValues[displayedValueIndex];
+ } else {
+ scrollSelectorValue = formatNumber(selectorIndex);
+ }
+ }
+ cache.put(selectorIndex, scrollSelectorValue);
+ }
+
+ private String formatNumber(int value) {
+ return (mFormatter != null) ? mFormatter.format(value) : formatNumberWithLocale(value);
+ }
+
+ private void validateInputTextView(View v) {
+ String str = String.valueOf(((TextView) v).getText());
+ if (TextUtils.isEmpty(str)) {
+ // Restore to the old value as we don't allow empty values
+ updateInputTextView();
+ } else {
+ // Check the new value and ensure it's in range
+ int current = getSelectedPos(str.toString());
+ setValueInternal(current, true);
+ }
+ }
+
+ /**
+ * Updates the view of this NumberPicker. If displayValues were specified in
+ * the string corresponding to the index specified by the current value will
+ * be returned. Otherwise, the formatter specified in {@link #setFormatter}
+ * will be used to format the number.
+ *
+ * @return Whether the text was updated.
+ */
+ private boolean updateInputTextView() {
+ /*
+ * 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.
+ */
+ String text = (mDisplayedValues == null) ? formatNumber(mValue)
+ : mDisplayedValues[mValue - mMinValue];
+ if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
+ mInputText.setText(text);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Notifies the listener, if registered, of a change of the value of this
+ * NumberPicker.
+ */
+ private void notifyChange(int previous, int current) {
+ if (mOnValueChangeListener != null) {
+ mOnValueChangeListener.onValueChange(this, previous, mValue);
+ }
+ }
+
+ /**
+ * Posts a command for changing the current value by one.
+ *
+ * @param increment Whether to increment or decrement the value.
+ */
+ private void postChangeCurrentByOneFromLongPress(boolean increment, long delayMillis) {
+ if (mChangeCurrentByOneFromLongPressCommand == null) {
+ mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand();
+ } else {
+ removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
+ }
+ mChangeCurrentByOneFromLongPressCommand.setStep(increment);
+ postDelayed(mChangeCurrentByOneFromLongPressCommand, delayMillis);
+ }
+
+ /**
+ * Removes the command for changing the current value by one.
+ */
+ private void removeChangeCurrentByOneFromLongPress() {
+ if (mChangeCurrentByOneFromLongPressCommand != null) {
+ removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
+ }
+ }
+
+ /**
+ * Posts a command for beginning an edit of the current value via IME on
+ * long press.
+ */
+ private void postBeginSoftInputOnLongPressCommand() {
+ if (mBeginSoftInputOnLongPressCommand == null) {
+ mBeginSoftInputOnLongPressCommand = new BeginSoftInputOnLongPressCommand();
+ } else {
+ removeCallbacks(mBeginSoftInputOnLongPressCommand);
+ }
+ postDelayed(mBeginSoftInputOnLongPressCommand, ViewConfiguration.getLongPressTimeout());
+ }
+
+ /**
+ * Removes the command for beginning an edit of the current value via IME.
+ */
+ private void removeBeginSoftInputCommand() {
+ if (mBeginSoftInputOnLongPressCommand != null) {
+ removeCallbacks(mBeginSoftInputOnLongPressCommand);
+ }
+ }
+
+ /**
+ * Removes all pending callback from the message queue.
+ */
+ private void removeAllCallbacks() {
+ if (mChangeCurrentByOneFromLongPressCommand != null) {
+ removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
+ }
+ if (mSetSelectionCommand != null) {
+ removeCallbacks(mSetSelectionCommand);
+ }
+ if (mBeginSoftInputOnLongPressCommand != null) {
+ removeCallbacks(mBeginSoftInputOnLongPressCommand);
+ }
+ mPressedStateHelper.cancel();
+ }
+
+ /**
+ * @return The selected index given its displayed value.
+ */
+ private int getSelectedPos(String value) {
+ if (mDisplayedValues == null) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ // Ignore as if it's not a number we don't care
+ }
+ } else {
+ for (int i = 0; i < mDisplayedValues.length; i++) {
+ // Don't force the user to type in jan when ja will do
+ value = value.toLowerCase();
+ if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
+ return mMinValue + 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(value);
+ } catch (NumberFormatException e) {
+
+ // Ignore as if it's not a number we don't care
+ }
+ }
+ return mMinValue;
+ }
+
+ /**
+ * Posts an {@link SetSelectionCommand} from the given selectionStart
+ * to selectionEnd.
+ */
+ private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
+ if (mSetSelectionCommand == null) {
+ mSetSelectionCommand = new SetSelectionCommand();
+ } else {
+ removeCallbacks(mSetSelectionCommand);
+ }
+ mSetSelectionCommand.mSelectionStart = selectionStart;
+ mSetSelectionCommand.mSelectionEnd = selectionEnd;
+ post(mSetSelectionCommand);
+ }
+
+ /**
+ * The numbers accepted by the input text's {@link Filter}
+ */
+ private static final char[] DIGIT_CHARACTERS = new char[] {
+ // Latin digits are the common case
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ // Arabic-Indic
+ '\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0665', '\u0666', '\u0667', '\u0668'
+ , '\u0669',
+ // Extended Arabic-Indic
+ '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8'
+ , '\u06f9'
+ };
+
+ /**
+ * Filter for accepting only valid indices or prefixes of the string
+ * representation of valid indices.
+ */
+ class InputTextFilter extends NumberKeyListener {
+
+ // XXX This doesn't allow for range limits when controlled by a
+ // soft input method!
+ public int getInputType() {
+ return InputType.TYPE_CLASS_TEXT;
+ }
+
+ @Override
+ protected char[] getAcceptedChars() {
+ return DIGIT_CHARACTERS;
+ }
+
+ @Override
+ public CharSequence filter(
+ CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
+ if (mDisplayedValues == null) {
+ 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 > mMaxValue) {
+ return "";
+ } else {
+ return filtered;
+ }
+ } else {
+ CharSequence filtered = String.valueOf(source.subSequence(start, end));
+ if (TextUtils.isEmpty(filtered)) {
+ return "";
+ }
+ String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
+ + dest.subSequence(dend, dest.length());
+ String str = String.valueOf(result).toLowerCase();
+ for (String val : mDisplayedValues) {
+ String valLowerCase = val.toLowerCase();
+ if (valLowerCase.startsWith(str)) {
+ postSetSelectionCommand(result.length(), val.length());
+ return val.subSequence(dstart, val.length());
+ }
+ }
+ return "";
+ }
+ }
+ }
+
+ /**
+ * Ensures that the scroll wheel is adjusted i.e. there is no offset and the
+ * middle element is in the middle of the widget.
+ *
+ * @return Whether an adjustment has been made.
+ */
+ private boolean ensureScrollWheelAdjusted() {
+ // adjust to the closest value
+ int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
+ if (deltaY != 0) {
+ mPreviousScrollerY = 0;
+ if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
+ deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
+ }
+ mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
+ invalidate();
+ return true;
+ }
+ return false;
+ }
+
+ class PressedStateHelper implements Runnable {
+ public static final int BUTTON_INCREMENT = 1;
+ public static final int BUTTON_DECREMENT = 2;
+
+ private final int MODE_PRESS = 1;
+ private final int MODE_TAPPED = 2;
+
+ private int mManagedButton;
+ private int mMode;
+
+ public void cancel() {
+ mMode = 0;
+ mManagedButton = 0;
+ NumberPicker.this.removeCallbacks(this);
+ if (mIncrementVirtualButtonPressed) {
+ mIncrementVirtualButtonPressed = false;
+ invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom());
+ }
+ mDecrementVirtualButtonPressed = false;
+ if (mDecrementVirtualButtonPressed) {
+ invalidate(0, 0, getRight(), mTopSelectionDividerTop);
+ }
+ }
+
+ public void buttonPressDelayed(int button) {
+ cancel();
+ mMode = MODE_PRESS;
+ mManagedButton = button;
+ NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout());
+ }
+
+ public void buttonTapped(int button) {
+ cancel();
+ mMode = MODE_TAPPED;
+ mManagedButton = button;
+ NumberPicker.this.post(this);
+ }
+
+ @Override
+ public void run() {
+ switch (mMode) {
+ case MODE_PRESS: {
+ switch (mManagedButton) {
+ case BUTTON_INCREMENT: {
+ mIncrementVirtualButtonPressed = true;
+ invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom());
+ } break;
+ case BUTTON_DECREMENT: {
+ mDecrementVirtualButtonPressed = true;
+ invalidate(0, 0, getRight(), mTopSelectionDividerTop);
+ }
+ }
+ } break;
+ case MODE_TAPPED: {
+ switch (mManagedButton) {
+ case BUTTON_INCREMENT: {
+ if (!mIncrementVirtualButtonPressed) {
+ NumberPicker.this.postDelayed(this,
+ ViewConfiguration.getPressedStateDuration());
+ }
+ mIncrementVirtualButtonPressed ^= true;
+ invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom());
+ } break;
+ case BUTTON_DECREMENT: {
+ if (!mDecrementVirtualButtonPressed) {
+ NumberPicker.this.postDelayed(this,
+ ViewConfiguration.getPressedStateDuration());
+ }
+ mDecrementVirtualButtonPressed ^= true;
+ invalidate(0, 0, getRight(), mTopSelectionDividerTop);
+ }
+ }
+ } break;
+ }
+ }
+ }
+
+ /**
+ * Command for setting the input text selection.
+ */
+ class SetSelectionCommand implements Runnable {
+ private int mSelectionStart;
+
+ private int mSelectionEnd;
+
+ public void run() {
+ mInputText.setSelection(mSelectionStart, mSelectionEnd);
+ }
+ }
+
+ /**
+ * Command for changing the current value from a long press by one.
+ */
+ class ChangeCurrentByOneFromLongPressCommand implements Runnable {
+ private boolean mIncrement;
+
+ private void setStep(boolean increment) {
+ mIncrement = increment;
+ }
+
+ @Override
+ public void run() {
+ changeValueByOne(mIncrement);
+ postDelayed(this, mLongPressUpdateInterval);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static class CustomEditText extends EditText {
+
+ public CustomEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onEditorAction(int actionCode) {
+ super.onEditorAction(actionCode);
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ clearFocus();
+ }
+ }
+ }
+
+ /**
+ * Command for beginning soft input on long press.
+ */
+ class BeginSoftInputOnLongPressCommand implements Runnable {
+
+ @Override
+ public void run() {
+ showSoftInput();
+ mIngonreMoveEvents = true;
+ }
+ }
+
+ private SupportAccessibilityNodeProvider getSupportAccessibilityNodeProvider() {
+ return new SupportAccessibilityNodeProvider();
+ }
+
+ class SupportAccessibilityNodeProvider {
+
+ AccessibilityNodeProviderImpl mProvider;
+
+ private SupportAccessibilityNodeProvider() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mProvider = new AccessibilityNodeProviderImpl();
+ }
+ }
+
+ public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+ if (mProvider != null) {
+ return mProvider.performAction(virtualViewId, action, arguments);
+ }
+
+ return false;
+ }
+
+ public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
+ if (mProvider != null) mProvider.sendAccessibilityEventForVirtualView(virtualViewId, eventType);
+ }
+ }
+
+ /**
+ * Class for managing virtual view tree rooted at this picker.
+ */
+ class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
+ private static final int UNDEFINED = Integer.MIN_VALUE;
+
+ private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
+
+ private static final int VIRTUAL_VIEW_ID_INPUT = 2;
+
+ private static final int VIRTUAL_VIEW_ID_DECREMENT = 3;
+
+ private final Rect mTempRect = new Rect();
+
+ private final int[] mTempArray = new int[2];
+
+ private int mAccessibilityFocusedView = UNDEFINED;
+
+ @Override
+ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+ switch (virtualViewId) {
+ case View.NO_ID:
+ return createAccessibilityNodeInfoForNumberPicker( getScrollX(), getScrollY(),
+ getScrollX() + (getRight() - getLeft()), getScrollY() + (getBottom() - getTop()));
+ case VIRTUAL_VIEW_ID_DECREMENT:
+ return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_DECREMENT,
+ getVirtualDecrementButtonText(), getScrollX(), getScrollY(),
+ getScrollX() + (getRight() - getLeft()),
+ mTopSelectionDividerTop + mSelectionDividerHeight);
+ case VIRTUAL_VIEW_ID_INPUT:
+ return createAccessibiltyNodeInfoForInputText();
+ case VIRTUAL_VIEW_ID_INCREMENT:
+ return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_INCREMENT,
+ getVirtualIncrementButtonText(), getScrollX(),
+ mBottomSelectionDividerBottom - mSelectionDividerHeight,
+ getScrollX() + (getRight() - getLeft()), getScrollY() + (getBottom() - getTop()));
+ }
+ return super.createAccessibilityNodeInfo(virtualViewId);
+ }
+
+ @Override
+ public List findAccessibilityNodeInfosByText(String searched,
+ int virtualViewId) {
+ if (TextUtils.isEmpty(searched)) {
+ return Collections.emptyList();
+ }
+ String searchedLowerCase = searched.toLowerCase();
+ List result = new ArrayList();
+ switch (virtualViewId) {
+ case View.NO_ID: {
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
+ VIRTUAL_VIEW_ID_DECREMENT, result);
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
+ VIRTUAL_VIEW_ID_INPUT, result);
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
+ VIRTUAL_VIEW_ID_INCREMENT, result);
+ return result;
+ }
+ case VIRTUAL_VIEW_ID_DECREMENT:
+ case VIRTUAL_VIEW_ID_INCREMENT:
+ case VIRTUAL_VIEW_ID_INPUT: {
+ findAccessibilityNodeInfosByTextInChild(searchedLowerCase, virtualViewId,
+ result);
+ return result;
+ }
+ }
+ return super.findAccessibilityNodeInfosByText(searched, virtualViewId);
+ }
+
+ @Override
+ public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+ switch (virtualViewId) {
+ case View.NO_ID: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ // requestAccessibilityFocus();
+ performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ // clearAccessibilityFocus();
+ performAccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
+ return true;
+ }
+ return false;
+ }
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ if (NumberPicker.this.isEnabled()
+ && (getWrapSelectorWheel() || getValue() < getMaxValue())) {
+ changeValueByOne(true);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ if (NumberPicker.this.isEnabled()
+ && (getWrapSelectorWheel() || getValue() > getMinValue())) {
+ changeValueByOne(false);
+ return true;
+ }
+ } return false;
+ }
+ } break;
+ case VIRTUAL_VIEW_ID_INPUT: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_FOCUS: {
+ if (NumberPicker.this.isEnabled() && !mInputText.isFocused()) {
+ return mInputText.requestFocus();
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+ if (NumberPicker.this.isEnabled() && mInputText.isFocused()) {
+ mInputText.clearFocus();
+ return true;
+ }
+ return false;
+ }
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ if (NumberPicker.this.isEnabled()) {
+ showSoftInput();
+ return true;
+ }
+ return false;
+ }
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ mInputText.invalidate();
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ mInputText.invalidate();
+ return true;
+ }
+ } return false;
+ default: {
+ return mInputText.performAccessibilityAction(action, arguments);
+ }
+ }
+ } return false;
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ if (NumberPicker.this.isEnabled()) {
+ NumberPicker.this.changeValueByOne(true);
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_CLICKED);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom());
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom());
+ return true;
+ }
+ } return false;
+ }
+ } return false;
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ if (NumberPicker.this.isEnabled()) {
+ final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT);
+ NumberPicker.this.changeValueByOne(increment);
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_CLICKED);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ invalidate(0, 0, getRight(), mTopSelectionDividerTop);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ invalidate(0, 0, getRight(), mTopSelectionDividerTop);
+ return true;
+ }
+ } return false;
+ }
+ } return false;
+ }
+ return super.performAction(virtualViewId, action, arguments);
+ }
+
+ public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
+ switch (virtualViewId) {
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ if (hasVirtualDecrementButton()) {
+ sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+ getVirtualDecrementButtonText());
+ }
+ } break;
+ case VIRTUAL_VIEW_ID_INPUT: {
+ sendAccessibilityEventForVirtualText(eventType);
+ } break;
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ if (hasVirtualIncrementButton()) {
+ sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+ getVirtualIncrementButtonText());
+ }
+ } break;
+ }
+ }
+
+ private void sendAccessibilityEventForVirtualText(int eventType) {
+ if (((AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE)).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ mInputText.onInitializeAccessibilityEvent(event);
+ mInputText.onPopulateAccessibilityEvent(event);
+ event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
+ }
+
+ private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
+ String text) {
+ if (((AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE)).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setClassName(Button.class.getName());
+ event.setPackageName(getContext().getPackageName());
+ event.getText().add(text);
+ event.setEnabled(NumberPicker.this.isEnabled());
+ event.setSource(NumberPicker.this, virtualViewId);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
+ }
+
+ private void findAccessibilityNodeInfosByTextInChild(String searchedLowerCase,
+ int virtualViewId, List outResult) {
+ switch (virtualViewId) {
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ String text = getVirtualDecrementButtonText();
+ if (!TextUtils.isEmpty(text)
+ && text.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT));
+ }
+ } return;
+ case VIRTUAL_VIEW_ID_INPUT: {
+ CharSequence text = mInputText.getText();
+ if (!TextUtils.isEmpty(text) &&
+ text.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
+ return;
+ }
+ CharSequence contentDesc = mInputText.getText();
+ if (!TextUtils.isEmpty(contentDesc) &&
+ contentDesc.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
+ return;
+ }
+ } break;
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ String text = getVirtualIncrementButtonText();
+ if (!TextUtils.isEmpty(text)
+ && text.toString().toLowerCase().contains(searchedLowerCase)) {
+ outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT));
+ }
+ } return;
+ }
+ }
+
+ private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() {
+ AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
+ info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
+ return info;
+ }
+
+ private AccessibilityNodeInfo createAccessibilityNodeInfoForVirtualButton(int virtualViewId,
+ String text, int left, int top, int right, int bottom) {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setClassName(Button.class.getName());
+ info.setPackageName(getContext().getPackageName());
+ info.setSource(NumberPicker.this, virtualViewId);
+ info.setParent(NumberPicker.this);
+ info.setText(text);
+ info.setClickable(true);
+ info.setLongClickable(true);
+ info.setEnabled(NumberPicker.this.isEnabled());
+ Rect boundsInParent = mTempRect;
+ boundsInParent.set(left, top, right, bottom);
+ // TODO info.setVisibleToUser(isVisibleToUser(boundsInParent));
+ info.setBoundsInParent(boundsInParent);
+ Rect boundsInScreen = boundsInParent;
+ int[] locationOnScreen = mTempArray;
+ getLocationOnScreen(locationOnScreen);
+ boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
+ info.setBoundsInScreen(boundsInScreen);
+
+ if (mAccessibilityFocusedView != virtualViewId) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ if (mAccessibilityFocusedView == virtualViewId) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
+ if (NumberPicker.this.isEnabled()) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ }
+
+ return info;
+ }
+
+ private AccessibilityNodeInfo createAccessibilityNodeInfoForNumberPicker(int left, int top,
+ int right, int bottom) {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setClassName(NumberPicker.class.getName());
+ info.setPackageName(getContext().getPackageName());
+ info.setSource(NumberPicker.this);
+
+ if (hasVirtualDecrementButton()) {
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
+ }
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ if (hasVirtualIncrementButton()) {
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
+ }
+
+ info.setParent((View) getParentForAccessibility());
+ info.setEnabled(NumberPicker.this.isEnabled());
+ info.setScrollable(true);
+
+ /** TODO: Figure out compat implementation for this
+ final float applicationScale =
+ getContext().getResources().getCompatibilityInfo().applicationScale;
+
+ Rect boundsInParent = mTempRect;
+ boundsInParent.set(left, top, right, bottom);
+ boundsInParent.scale(applicationScale);
+ info.setBoundsInParent(boundsInParent);
+
+ info.setVisibleToUser(isVisibleToUser());
+
+ Rect boundsInScreen = boundsInParent;
+ int[] locationOnScreen = mTempArray;
+ getLocationOnScreen(locationOnScreen);
+ boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
+ boundsInScreen.scale(applicationScale);
+ info.setBoundsInScreen(boundsInScreen);
+ */
+
+ if (mAccessibilityFocusedView != View.NO_ID) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ if (mAccessibilityFocusedView == View.NO_ID) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
+ if (NumberPicker.this.isEnabled()) {
+ if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ }
+ if (getWrapSelectorWheel() || getValue() > getMinValue()) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ }
+ }
+
+ return info;
+ }
+
+ private boolean hasVirtualDecrementButton() {
+ return getWrapSelectorWheel() || getValue() > getMinValue();
+ }
+
+ private boolean hasVirtualIncrementButton() {
+ return getWrapSelectorWheel() || getValue() < getMaxValue();
+ }
+
+ private String getVirtualDecrementButtonText() {
+ int value = mValue - 1;
+ if (mWrapSelectorWheel) {
+ value = getWrappedSelectorIndex(value);
+ }
+ if (value >= mMinValue) {
+ return (mDisplayedValues == null) ? formatNumber(value)
+ : mDisplayedValues[value - mMinValue];
+ }
+ return null;
+ }
+
+ private String getVirtualIncrementButtonText() {
+ int value = mValue + 1;
+ if (mWrapSelectorWheel) {
+ value = getWrappedSelectorIndex(value);
+ }
+ if (value <= mMaxValue) {
+ return (mDisplayedValues == null) ? formatNumber(value)
+ : mDisplayedValues[value - mMinValue];
+ }
+ return null;
+ }
+ }
+
+ static private String formatNumberWithLocale(int value) {
+ return String.format(Locale.getDefault(), "%d", value);
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/android/widget/numberpicker/backport/Scroller.java b/library/src/main/java/android/widget/numberpicker/backport/Scroller.java
new file mode 100644
index 0000000..d033c23
--- /dev/null
+++ b/library/src/main/java/android/widget/numberpicker/backport/Scroller.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2006 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 android.widget.numberpicker.backport;
+
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.util.FloatMath;
+import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+
+/**
+ * This class encapsulates scrolling. The duration of the scroll
+ * can be passed in the constructor and specifies the maximum time that
+ * the scrolling animation should take. Past this time, the scrolling is
+ * automatically moved to its final stage and computeScrollOffset()
+ * will always return false to indicate that scrolling is over.
+ */
+public class Scroller {
+ private int mMode;
+
+ private int mStartX;
+ private int mStartY;
+ private int mFinalX;
+ private int mFinalY;
+
+ private int mMinX;
+ private int mMaxX;
+ private int mMinY;
+ private int mMaxY;
+
+ private int mCurrX;
+ private int mCurrY;
+ private long mStartTime;
+ private int mDuration;
+ private float mDurationReciprocal;
+ private float mDeltaX;
+ private float mDeltaY;
+ private boolean mFinished;
+ private Interpolator mInterpolator;
+ private boolean mFlywheel;
+
+ private float mVelocity;
+
+ private static final int DEFAULT_DURATION = 250;
+ private static final int SCROLL_MODE = 0;
+ private static final int FLING_MODE = 1;
+
+ private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
+ private static float ALPHA = 800; // pixels / seconds
+ private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
+ private static float END_TENSION = 1.0f - START_TENSION;
+ private static final int NB_SAMPLES = 100;
+ private static final float[] SPLINE = new float[NB_SAMPLES + 1];
+
+ private float mDeceleration;
+ private final float mPpi;
+
+ static {
+ float x_min = 0.0f;
+ for (int i = 0; i <= NB_SAMPLES; i++) {
+ final float t = (float) i / NB_SAMPLES;
+ float x_max = 1.0f;
+ float x, tx, coef;
+ while (true) {
+ x = x_min + (x_max - x_min) / 2.0f;
+ coef = 3.0f * x * (1.0f - x);
+ tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
+ if (Math.abs(tx - t) < 1E-5) break;
+ if (tx > t) x_max = x;
+ else x_min = x;
+ }
+ final float d = coef + x * x * x;
+ SPLINE[i] = d;
+ }
+ SPLINE[NB_SAMPLES] = 1.0f;
+
+ // This controls the viscous fluid effect (how much of it)
+ sViscousFluidScale = 8.0f;
+ // must be set to 1.0 (used in viscousFluid())
+ sViscousFluidNormalize = 1.0f;
+ sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+ }
+
+ private static float sViscousFluidScale;
+ private static float sViscousFluidNormalize;
+
+ /**
+ * Create a Scroller with the default duration and interpolator.
+ */
+ public Scroller(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Create a Scroller with the specified interpolator. If the interpolator is
+ * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
+ * be in effect for apps targeting Honeycomb or newer.
+ */
+ public Scroller(Context context, Interpolator interpolator) {
+ this(context, interpolator, true);
+ }
+
+ /**
+ * Create a Scroller with the specified interpolator. If the interpolator is
+ * null, the default (viscous) interpolator will be used. Specify whether or
+ * not to support progressive "flywheel" behavior in flinging.
+ */
+ public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
+ mFinished = true;
+ mInterpolator = interpolator;
+ mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
+ mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
+ mFlywheel = flywheel;
+ }
+
+ /**
+ * The amount of friction applied to flings. The default value
+ * is {@link android.view.ViewConfiguration#getScrollFriction}.
+ *
+ * @param friction A scalar dimension-less value representing the coefficient of
+ * friction.
+ */
+ public final void setFriction(float friction) {
+ mDeceleration = computeDeceleration(friction);
+ }
+
+ private float computeDeceleration(float friction) {
+ return SensorManager.GRAVITY_EARTH // g (m/s^2)
+ * 39.37f // inch/meter
+ * mPpi // pixels per inch
+ * friction;
+ }
+
+ /**
+ *
+ * Returns whether the scroller has finished scrolling.
+ *
+ * @return True if the scroller has finished scrolling, false otherwise.
+ */
+ public final boolean isFinished() {
+ return mFinished;
+ }
+
+ /**
+ * Force the finished field to a particular value.
+ *
+ * @param finished The new finished value.
+ */
+ public final void forceFinished(boolean finished) {
+ mFinished = finished;
+ }
+
+ /**
+ * Returns how long the scroll event will take, in milliseconds.
+ *
+ * @return The duration of the scroll in milliseconds.
+ */
+ public final int getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Returns the current X offset in the scroll.
+ *
+ * @return The new X offset as an absolute distance from the origin.
+ */
+ public final int getCurrX() {
+ return mCurrX;
+ }
+
+ /**
+ * Returns the current Y offset in the scroll.
+ *
+ * @return The new Y offset as an absolute distance from the origin.
+ */
+ public final int getCurrY() {
+ return mCurrY;
+ }
+
+ /**
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration. Result may be
+ * negative.
+ */
+ public float getCurrVelocity() {
+ return mVelocity - mDeceleration * timePassed() / 2000.0f;
+ }
+
+ /**
+ * Returns the start X offset in the scroll.
+ *
+ * @return The start X offset as an absolute distance from the origin.
+ */
+ public final int getStartX() {
+ return mStartX;
+ }
+
+ /**
+ * Returns the start Y offset in the scroll.
+ *
+ * @return The start Y offset as an absolute distance from the origin.
+ */
+ public final int getStartY() {
+ return mStartY;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final X offset as an absolute distance from the origin.
+ */
+ public final int getFinalX() {
+ return mFinalX;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final Y offset as an absolute distance from the origin.
+ */
+ public final int getFinalY() {
+ return mFinalY;
+ }
+
+ /**
+ * Call this when you want to know the new location. If it returns true,
+ * the animation is not yet finished. loc will be altered to provide the
+ * new location.
+ */
+ public boolean computeScrollOffset() {
+ if (mFinished) {
+ return false;
+ }
+
+ int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+
+ if (timePassed < mDuration) {
+ switch (mMode) {
+ case SCROLL_MODE:
+ float x = timePassed * mDurationReciprocal;
+
+ if (mInterpolator == null)
+ x = viscousFluid(x);
+ else
+ x = mInterpolator.getInterpolation(x);
+
+ mCurrX = mStartX + Math.round(x * mDeltaX);
+ mCurrY = mStartY + Math.round(x * mDeltaY);
+ break;
+ case FLING_MODE:
+ final float t = (float) timePassed / mDuration;
+ final int index = (int) (NB_SAMPLES * t);
+ final float t_inf = (float) index / NB_SAMPLES;
+ final float t_sup = (float) (index + 1) / NB_SAMPLES;
+ final float d_inf = SPLINE[index];
+ final float d_sup = SPLINE[index + 1];
+ final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
+
+ mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
+ // Pin to mMinX <= mCurrX <= mMaxX
+ mCurrX = Math.min(mCurrX, mMaxX);
+ mCurrX = Math.max(mCurrX, mMinX);
+
+ mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
+ // Pin to mMinY <= mCurrY <= mMaxY
+ mCurrY = Math.min(mCurrY, mMaxY);
+ mCurrY = Math.max(mCurrY, mMinY);
+
+ if (mCurrX == mFinalX && mCurrY == mFinalY) {
+ mFinished = true;
+ }
+
+ break;
+ }
+ }
+ else {
+ mCurrX = mFinalX;
+ mCurrY = mFinalY;
+ mFinished = true;
+ }
+ return true;
+ }
+
+ /**
+ * Start scrolling by providing a starting point and the distance to travel.
+ * The scroll will use the default value of 250 milliseconds for the
+ * duration.
+ *
+ * @param startX Starting horizontal scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param startY Starting vertical scroll offset in pixels. Positive numbers
+ * will scroll the content up.
+ * @param dx Horizontal distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param dy Vertical distance to travel. Positive numbers will scroll the
+ * content up.
+ */
+ public void startScroll(int startX, int startY, int dx, int dy) {
+ startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
+ }
+
+ /**
+ * Start scrolling by providing a starting point and the distance to travel.
+ *
+ * @param startX Starting horizontal scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param startY Starting vertical scroll offset in pixels. Positive numbers
+ * will scroll the content up.
+ * @param dx Horizontal distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param dy Vertical distance to travel. Positive numbers will scroll the
+ * content up.
+ * @param duration Duration of the scroll in milliseconds.
+ */
+ public void startScroll(int startX, int startY, int dx, int dy, int duration) {
+ mMode = SCROLL_MODE;
+ mFinished = false;
+ mDuration = duration;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mStartX = startX;
+ mStartY = startY;
+ mFinalX = startX + dx;
+ mFinalY = startY + dy;
+ mDeltaX = dx;
+ mDeltaY = dy;
+ mDurationReciprocal = 1.0f / (float) mDuration;
+ }
+
+ /**
+ * Start scrolling based on a fling gesture. The distance travelled will
+ * depend on the initial velocity of the fling.
+ *
+ * @param startX Starting point of the scroll (X)
+ * @param startY Starting point of the scroll (Y)
+ * @param velocityX Initial velocity of the fling (X) measured in pixels per
+ * second.
+ * @param velocityY Initial velocity of the fling (Y) measured in pixels per
+ * second
+ * @param minX Minimum X value. The scroller will not scroll past this
+ * point.
+ * @param maxX Maximum X value. The scroller will not scroll past this
+ * point.
+ * @param minY Minimum Y value. The scroller will not scroll past this
+ * point.
+ * @param maxY Maximum Y value. The scroller will not scroll past this
+ * point.
+ */
+ public void fling(int startX, int startY, int velocityX, int velocityY,
+ int minX, int maxX, int minY, int maxY) {
+ // Continue a scroll or fling in progress
+ if (mFlywheel && !mFinished) {
+ float oldVel = getCurrVelocity();
+
+ float dx = (float) (mFinalX - mStartX);
+ float dy = (float) (mFinalY - mStartY);
+ float hyp = FloatMath.sqrt(dx * dx + dy * dy);
+
+ float ndx = dx / hyp;
+ float ndy = dy / hyp;
+
+ float oldVelocityX = ndx * oldVel;
+ float oldVelocityY = ndy * oldVel;
+ if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
+ Math.signum(velocityY) == Math.signum(oldVelocityY)) {
+ velocityX += oldVelocityX;
+ velocityY += oldVelocityY;
+ }
+ }
+
+ mMode = FLING_MODE;
+ mFinished = false;
+
+ float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
+
+ mVelocity = velocity;
+ final double l = Math.log(START_TENSION * velocity / ALPHA);
+ mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mStartX = startX;
+ mStartY = startY;
+
+ float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
+ float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
+
+ int totalDistance =
+ (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
+
+ mMinX = minX;
+ mMaxX = maxX;
+ mMinY = minY;
+ mMaxY = maxY;
+
+ mFinalX = startX + Math.round(totalDistance * coeffX);
+ // Pin to mMinX <= mFinalX <= mMaxX
+ mFinalX = Math.min(mFinalX, mMaxX);
+ mFinalX = Math.max(mFinalX, mMinX);
+
+ mFinalY = startY + Math.round(totalDistance * coeffY);
+ // Pin to mMinY <= mFinalY <= mMaxY
+ mFinalY = Math.min(mFinalY, mMaxY);
+ mFinalY = Math.max(mFinalY, mMinY);
+ }
+
+ static float viscousFluid(float x)
+ {
+ x *= sViscousFluidScale;
+ if (x < 1.0f) {
+ x -= (1.0f - (float)Math.exp(-x));
+ } else {
+ float start = 0.36787944117f; // 1/e == exp(-1)
+ x = 1.0f - (float)Math.exp(1.0f - x);
+ x = start + x * (1.0f - start);
+ }
+ x *= sViscousFluidNormalize;
+ return x;
+ }
+
+ /**
+ * Stops the animation. Contrary to {@link #forceFinished(boolean)},
+ * aborting the animating cause the scroller to move to the final x and y
+ * position
+ *
+ * @see #forceFinished(boolean)
+ */
+ public void abortAnimation() {
+ mCurrX = mFinalX;
+ mCurrY = mFinalY;
+ mFinished = true;
+ }
+
+ /**
+ * Extend the scroll animation. This allows a running animation to scroll
+ * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
+ *
+ * @param extend Additional time to scroll in milliseconds.
+ * @see #setFinalX(int)
+ * @see #setFinalY(int)
+ */
+ public void extendDuration(int extend) {
+ int passed = timePassed();
+ mDuration = passed + extend;
+ mDurationReciprocal = 1.0f / mDuration;
+ mFinished = false;
+ }
+
+ /**
+ * Returns the time elapsed since the beginning of the scrolling.
+ *
+ * @return The elapsed time in milliseconds.
+ */
+ public int timePassed() {
+ return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+ }
+
+ /**
+ * Sets the final position (X) for this scroller.
+ *
+ * @param newX The new X offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalY(int)
+ */
+ public void setFinalX(int newX) {
+ mFinalX = newX;
+ mDeltaX = mFinalX - mStartX;
+ mFinished = false;
+ }
+
+ /**
+ * Sets the final position (Y) for this scroller.
+ *
+ * @param newY The new Y offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalX(int)
+ */
+ public void setFinalY(int newY) {
+ mFinalY = newY;
+ mDeltaY = mFinalY - mStartY;
+ mFinished = false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isScrollingInDirection(float xvel, float yvel) {
+ return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&
+ Math.signum(yvel) == Math.signum(mFinalY - mStartY);
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogBuilder.java b/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogBuilder.java
index 38f0c9c..e044a0f 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogBuilder.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogBuilder.java
@@ -1,94 +1,290 @@
package eu.inmite.android.lib.dialogs;
import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
+import android.text.Html;
+import android.text.SpannedString;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
/**
* Internal base builder that holds common values for all dialog fragment builders.
*
- * @author Tomas Vondracek
+ * @author Tomas Vondracek (inmite.eu), Michal Reiter (avast.com)
*/
-abstract class BaseDialogBuilder> {
-
- public final static String ARG_REQUEST_CODE = "request_code";
- public final static String ARG_CANCELABLE_ON_TOUCH_OUTSIDE = "cancelable_oto";
- public final static String DEFAULT_TAG = "simple_dialog";
- public final static int DEFAULT_REQUEST_CODE = -42;
-
- protected final Context mContext;
- protected final FragmentManager mFragmentManager;
- protected final Class extends BaseDialogFragment> mClass;
-
- private Fragment mTargetFragment;
- private boolean mCancelable = true;
- private boolean mCancelableOnTouchOutside = true;
-
- private String mTag = DEFAULT_TAG;
- private int mRequestCode = DEFAULT_REQUEST_CODE;
-
- public BaseDialogBuilder(Context context, FragmentManager fragmentManager, Class extends BaseDialogFragment> clazz) {
- mFragmentManager = fragmentManager;
- mContext = context.getApplicationContext();
- mClass = clazz;
- }
-
- protected abstract T self();
-
- protected abstract Bundle prepareArguments();
-
- public T setCancelable(boolean cancelable) {
- mCancelable = cancelable;
- return self();
- }
-
- public T setCancelableOnTouchOutside(boolean cancelable) {
- mCancelableOnTouchOutside = cancelable;
- if (cancelable) {
- mCancelable = cancelable;
- }
- return self();
- }
-
- public T setTargetFragment(Fragment fragment, int requestCode) {
- mTargetFragment = fragment;
- mRequestCode = requestCode;
- return self();
- }
-
- public T setRequestCode(int requestCode) {
- mRequestCode = requestCode;
- return self();
- }
-
- public T setTag(String tag) {
- mTag = tag;
- return self();
- }
+public abstract class BaseDialogBuilder> {
+
+ protected final FragmentManager mFragmentManager;
+ protected final Class extends BaseDialogFragment> mClass;
+ protected final DialogParams mDialogParams;
+
+ private Fragment mTargetFragment;
+
+ /**
+ * Public constructor.
+ *
+ * @param context Context
+ * @param fragmentManager {@link android.support.v4.app.FragmentManager}
+ * @param clazz calling class
+ */
+ public BaseDialogBuilder(Context context, FragmentManager fragmentManager,
+ Class extends BaseDialogFragment> clazz) {
+ mFragmentManager = fragmentManager;
+ mClass = clazz;
+ mDialogParams = new DialogParams(context);
+ }
+
+ protected abstract T self();
+
+ protected abstract Bundle prepareArguments();
+
+ /**
+ * Sets whether dialog can be cancelable.
+ *
+ * @param cancelable true/false (default is true)
+ * @return builder
+ */
+ public T setCancelable(boolean cancelable) {
+ mDialogParams.cancelable = cancelable;
+ return self();
+ }
+
+ /**
+ * Sets whether dialog can be canceled on touch outside.
+ *
+ * @param cancelable true/false (default is true)
+ * @return builder
+ */
+ public T setCancelableOnTouchOutside(boolean cancelable) {
+ mDialogParams.cancelableOnTouchOutside = cancelable;
+ if (cancelable) {
+ mDialogParams.cancelable = cancelable;
+ }
+ return self();
+ }
+
+ /**
+ * Sets target fragment.
+ *
+ * @param fragment target fragment
+ * @param requestCode code to identify dialog
+ * @return builder
+ */
+ public T setTargetFragment(Fragment fragment, int requestCode) {
+ mTargetFragment = fragment;
+ mDialogParams.requestCode = requestCode;
+ return self();
+ }
+
+ /**
+ * Sets request code.
+ *
+ * @param requestCode code to identify dialog
+ * @return builder
+ */
+ public T setRequestCode(int requestCode) {
+ mDialogParams.requestCode = requestCode;
+ return self();
+ }
+
+ /**
+ * Sets tag for {@link android.support.v4.app.FragmentTransaction}.
+ *
+ * @param tag fragment tag
+ * @return builder
+ */
+ public T setTag(String tag) {
+ mDialogParams.tag = tag;
+ return self();
+ }
+
+ /**
+ * Sets message.
+ *
+ * @param messageResourceId dialog message
+ * @return builder
+ */
+ public T setMessage(int messageResourceId) {
+ mDialogParams.message = mDialogParams.context.getString(messageResourceId);
+ return self();
+ }
+
+ /**
+ * Sets message.
+ *
+ * Allow to set resource string with HTML formatting and bind %s,%i. This is workaround for
+ * https://code.google.com/p/android/issues/detail?id=2923
+ *
+ * @param resourceId dialog message with binders %s, %i
+ * @param formatArgs formatting params for previous string
+ * @return builder
+ */
+ public T setMessage(int resourceId, Object... formatArgs) {
+ mDialogParams.message = Html.fromHtml(String.format(Html.toHtml(new SpannedString(
+ mDialogParams.context.getString(resourceId))), formatArgs));
+ return self();
+ }
+
+ /**
+ * Sets message.
+ *
+ * @param message dialog message
+ * @return builder
+ */
+ public T setMessage(CharSequence message) {
+ mDialogParams.message = message;
+ return self();
+ }
+
+ /**
+ * Sets message.
+ *
+ * @param message dialog message
+ * @return builder
+ */
+ public T setMessage(String message) {
+ mDialogParams.message = message;
+ return self();
+ }
+
+ /**
+ * Sets positive button text.
+ *
+ * @param textResourceId dialog label
+ * @return builder
+ */
+ public T setPositiveButtonText(int textResourceId) {
+ mDialogParams.positiveButtonText = mDialogParams.context.getString(textResourceId);
+ return self();
+ }
+
+ /**
+ * Sets positive button text.
+ *
+ * @param text button label
+ * @return builder
+ */
+ public T setPositiveButtonText(String text) {
+ mDialogParams.positiveButtonText = text;
+ return self();
+ }
+
+ /**
+ * Sets negative button text.
+ *
+ * @param textResourceId button label
+ * @return builder
+ */
+ public T setNegativeButtonText(int textResourceId) {
+ mDialogParams.negativeButtonText = mDialogParams.context.getString(textResourceId);
+ return self();
+ }
+
+ /**
+ * Sets negative button text.
+ *
+ * @param text button label
+ * @return builder
+ */
+ public T setNegativeButtonText(String text) {
+ mDialogParams.negativeButtonText = text;
+ return self();
+ }
+
+ /**
+ * Sets neutral button text.
+ *
+ * @param textResourceId button label
+ * @return builder
+ */
+ public T setNeutralButtonText(int textResourceId) {
+ mDialogParams.neutralButtonText = mDialogParams.context.getString(textResourceId);
+ return self();
+ }
+
+ /**
+ * Sets neutral button text.
+ *
+ * @param text button label
+ * @return builder
+ */
+ public T setNeutralButtonText(String text) {
+ mDialogParams.neutralButtonText = text;
+ return self();
+ }
+
+ /**
+ * Sets title.
+ *
+ * @param titleResourceId dialog label
+ * @return builder
+ */
+ public T setTitle(int titleResourceId) {
+ mDialogParams.title = mDialogParams.context.getString(titleResourceId);
+ return self();
+ }
+
+ /**
+ * Sets title.
+ *
+ * @param title dialog label
+ * @return builder
+ */
+ public T setTitle(String title) {
+ mDialogParams.title = title;
+ return self();
+ }
+
+ /**
+ * Sets title.
+ *
+ * @param title dialog label
+ * @return builder
+ */
+ public T setTitle(CharSequence title) {
+ mDialogParams.title = title.toString();
+ return self();
+ }
+
+ /**
+ * When there is neither positive nor negative button, default "close" button is created if it was
+ * enabled. Default is true.
+ */
+ public T hideDefaultButton(boolean hide) {
+ mDialogParams.showDefaultButton = !hide;
+ return self();
+ }
private BaseDialogFragment create() {
final Bundle args = prepareArguments();
- final BaseDialogFragment fragment = (BaseDialogFragment) Fragment.instantiate(mContext, mClass.getName(), args);
+ final BaseDialogFragment fragment = (BaseDialogFragment)Fragment.instantiate(mDialogParams.context, mClass.getName
+ (), args);
- args.putBoolean(ARG_CANCELABLE_ON_TOUCH_OUTSIDE, mCancelableOnTouchOutside);
+ args.putBoolean(BaseDialogFragment.ARG_CANCELABLE_ON_TOUCH_OUTSIDE, mDialogParams.cancelableOnTouchOutside);
if (mTargetFragment != null) {
- fragment.setTargetFragment(mTargetFragment, mRequestCode);
+ fragment.setTargetFragment(mTargetFragment, mDialogParams.requestCode);
} else {
- args.putInt(ARG_REQUEST_CODE, mRequestCode);
+ args.putInt(BaseDialogFragment.ARG_REQUEST_CODE, mDialogParams.requestCode);
}
- fragment.setCancelable(mCancelable);
+ fragment.setCancelable(mDialogParams.cancelable);
return fragment;
}
- public DialogFragment show() {
+ public DialogFragment show() {
BaseDialogFragment fragment = create();
- fragment.show(mFragmentManager, mTag);
- return fragment;
- }
+ fragment.show(mFragmentManager, mDialogParams.tag);
+ return fragment;
+ }
/**
* Like show() but allows the commit to be executed after an activity's state is saved. This
@@ -98,7 +294,73 @@ public DialogFragment show() {
*/
public DialogFragment showAllowingStateLoss() {
BaseDialogFragment fragment = create();
- fragment.showAllowingStateLoss(mFragmentManager, mTag);
+ fragment.showAllowingStateLoss(mFragmentManager, mDialogParams.tag);
return fragment;
}
+
+ /**
+ * Holds params for dialog similarly as in {@link AlertController.AlertParams}
+ */
+ public static class DialogParams {
+ public final Context context;
+
+ public CharSequence title = null;
+ public CharSequence message;
+ public CharSequence positiveButtonText;
+ public CharSequence negativeButtonText;
+ public CharSequence neutralButtonText;
+ public String[] stringItems;
+ public String[] multiChoiceItems;
+ public boolean[] multiChoiceSelectedItems;
+ public String tag = BaseDialogFragment.DEFAULT_TAG;
+
+ public View.OnClickListener positiveButtonListener;
+ public View.OnClickListener negativeButtonListener;
+ public View.OnClickListener neutralButtonListener;
+
+ public DialogFragment dialogFragment;
+ public ViewGroup container;
+ public LayoutInflater layoutInflater;
+ public View view;
+
+ public boolean cancelable = true;
+ public boolean cancelableOnTouchOutside = true;
+ public boolean viewSpacingSpecified;
+ public boolean showDefaultButton = true;
+
+ public int viewSpacingLeft;
+ public int viewSpacingTop;
+ public int requestCode = BaseDialogFragment.DEFAULT_REQUEST_CODE;
+ public int viewSpacingRight;
+ public int viewSpacingBottom;
+ public ListAdapter listAdapter;
+ public int listCheckedItemIdx;
+ public AdapterView.OnItemClickListener onItemClickListener;
+ public Drawable icon;
+
+ public ColorStateList buttonTextColor;
+
+ public int titleTextColor;
+ public int titleSeparatorColor;
+ public int messageTextColor;
+
+ public int buttonSeparatorColor;
+ public int buttonBackgroundColorNormal;
+ public int buttonBackgroundColorPressed;
+ public int buttonBackgroundColorFocused;
+
+ public int listItemSeparatorColor;
+ public int listItemBackgroundColorNormal;
+ public int listItemBackgroundColorPressed;
+ public int listItemBackgroundColorFocused;
+
+ private DialogParams() {
+ context = null;
+ }
+
+ public DialogParams(Context context) {
+ this.context = context;
+ }
+
+ }
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogFragment.java
index 06012dc..33f0de2 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogFragment.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/BaseDialogFragment.java
@@ -29,57 +29,107 @@
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
+import android.widget.*;
/**
- * Base dialog fragment for all your dialogs, stylable and same design on Android 2.2+.
+ * Base dialog fragment for all your dialogs, styleable and same design on Android 2.2+.
*
* @author David Vávra (david@inmite.eu)
*/
public abstract class BaseDialogFragment extends DialogFragment {
+ /**
+ * Bundle argument tags to identify dialog window.
+ */
+ public final static String ARG_REQUEST_CODE = "request_code";
+ public final static int DEFAULT_REQUEST_CODE = -42;
+
+ /**
+ * Bundle argument tags for buttons, dialog message and title.
+ */
+ public final static String ARG_MESSAGE = "message";
+ public final static String ARG_TITLE = "title";
+ public final static String ARG_POSITIVE_BUTTON = "positive_button";
+ public final static String ARG_NEGATIVE_BUTTON = "negative_button";
+ public final static String ARG_NEUTRAL_BUTTON = "neutral_button";
+
+ /**
+ * Bundle argument tag for cancellation param.
+ */
+ public final static String ARG_CANCELABLE_ON_TOUCH_OUTSIDE = "cancelable_oto";
+ public final static String ARG_CANCELABLE = "cancelable";
+
+ /**
+ * Bundle argument for list items.
+ */
+ public final static String ARG_ITEMS = "items";
+
+ /**
+ * Bundle arguments for multi choice list items.
+ */
+ public final static String ARG_ITEMS_MULTICHOICE = "items_multichoice";
+ public final static String ARG_ITEMS_SELECTED_MULTICHOICE = "items_selected_multichoice";
+
+ /**
+ * Default fragment tag.
+ */
+ public final static String DEFAULT_TAG = "simple_dialog";
+
+ /**
+ * States for list items
+ */
+ private final static int[] pressedState = {android.R.attr.state_pressed};
+ private final static int[] focusedState = {android.R.attr.state_focused};
+ private final static int[] defaultState = {android.R.attr.state_enabled};
+ /**
+ * Identification of caller
+ */
+ protected int mRequestCode;
+
+ /**
+ * Children should extend this to add more things to base builder.
+ *
+ * Typically {@code BaseDialogBuilder.setTitle} and {@code BaseDialogBuilder.setMessage} and
+ * {@code BaseDialogBuilder.setPositiveButton} are called here.
+ */
+ protected abstract Builder build(Builder initialBuilder);
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- Dialog dialog = new Dialog(getActivity(), R.style.SDL_Dialog);
- // custom dialog background
- final TypedArray a = getActivity().getTheme()
- .obtainStyledAttributes(null, R.styleable.DialogStyle, R.attr.sdlDialogStyle, 0);
- Drawable dialogBackground = a.getDrawable(R.styleable.DialogStyle_dialogBackground);
- a.recycle();
- dialog.getWindow().setBackgroundDrawable(dialogBackground);
- Bundle args = getArguments();
- if (args != null) {
- dialog.setCanceledOnTouchOutside(
- args.getBoolean(BaseDialogBuilder.ARG_CANCELABLE_ON_TOUCH_OUTSIDE));
- }
- return dialog;
+ Dialog dialog = new Dialog(getActivity(), R.style.SDL_Dialog);
+ // custom dialog background
+ final TypedArray a = getActivity().getTheme().obtainStyledAttributes(null,
+ R.styleable.DialogStyle, R.attr.sdlDialogStyle, 0);
+ Drawable dialogBackground = a.getDrawable(R.styleable.DialogStyle_dialogBackground);
+ a.recycle();
+ dialog.getWindow().setBackgroundDrawable(dialogBackground);
+ Bundle args = getArguments();
+ if (args != null) {
+ boolean cancelable = args.getBoolean(ARG_CANCELABLE);
+ dialog.setCancelable(cancelable);
+ dialog.setCanceledOnTouchOutside(args.getBoolean(ARG_CANCELABLE_ON_TOUCH_OUTSIDE));
+ }
+ return dialog;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Builder builder = new Builder(this, getActivity(), inflater, container);
- return build(builder).create();
+ Bundle savedInstanceState) {
+ Builder builder = new Builder(this, getActivity(), inflater, container);
+ return build(builder).create();
}
- protected abstract Builder build(Builder initialBuilder);
-
@Override
public void onDestroyView() {
- // bug in the compatibility library
- if (getDialog() != null && getRetainInstance()) {
- getDialog().setDismissMessage(null);
- }
- super.onDestroyView();
+ // bug in the compatibility library
+ if (getDialog() != null && getRetainInstance()) {
+ getDialog().setDismissMessage(null);
+ }
+ super.onDestroyView();
}
public void showAllowingStateLoss(FragmentManager manager, String tag) {
@@ -92,465 +142,697 @@ public void showAllowingStateLoss(FragmentManager manager, String tag) {
* @return the positive button if specified and the view is created, null otherwise
*/
protected Button getPositiveButton() {
- if (getView() != null) {
- return (Button) getView().findViewById(R.id.sdl__positive_button);
- } else {
- return null;
- }
+ if (getView() != null) {
+ return (Button)getView().findViewById(R.id.sdl__positive_button);
+ } else {
+ return null;
+ }
}
/**
* @return the negative button if specified and the view is created, null otherwise
*/
protected Button getNegativeButton() {
- if (getView() != null) {
- return (Button) getView().findViewById(R.id.sdl__negative_button);
- } else {
- return null;
- }
+ if (getView() != null) {
+ return (Button)getView().findViewById(R.id.sdl__negative_button);
+ } else {
+ return null;
+ }
}
/**
* @return the neutral button if specified and the view is created, null otherwise
*/
protected Button getNeutralButton() {
- if (getView() != null) {
- return (Button) getView().findViewById(R.id.sdl__neutral_button);
- } else {
- return null;
- }
+ if (getView() != null) {
+ return (Button)getView().findViewById(R.id.sdl__neutral_button);
+ } else {
+ return null;
+ }
}
/**
- * Custom dialog builder
+ * Returns the dialog message.
+ *
+ * @return message
*/
- protected static class Builder {
-
- private final DialogFragment mDialogFragment;
-
- private final Context mContext;
-
- private final ViewGroup mContainer;
-
- private final LayoutInflater mInflater;
-
- private CharSequence mTitle = null;
-
- private CharSequence mPositiveButtonText;
-
- private View.OnClickListener mPositiveButtonListener;
-
- private CharSequence mNegativeButtonText;
-
- private View.OnClickListener mNegativeButtonListener;
-
- private CharSequence mNeutralButtonText;
-
- private View.OnClickListener mNeutralButtonListener;
-
- private CharSequence mMessage;
-
- private View mView;
-
- private boolean mViewSpacingSpecified;
-
- private int mViewSpacingLeft;
-
- private int mViewSpacingTop;
-
- private int mViewSpacingRight;
-
- private int mViewSpacingBottom;
-
- private ListAdapter mListAdapter;
-
- private int mListCheckedItemIdx;
-
- private AdapterView.OnItemClickListener mOnItemClickListener;
-
- private Drawable mIcon;
-
- /**
- * Styling: *
- */
- private int mTitleTextColor;
-
- private int mTitleSeparatorColor;
-
- private int mMessageTextColor;
-
- private ColorStateList mButtonTextColor;
-
- private int mButtonSeparatorColor;
-
- private int mButtonBackgroundColorNormal;
-
- private int mButtonBackgroundColorPressed;
-
- private int mButtonBackgroundColorFocused;
-
- private int mListItemSeparatorColor;
-
- private int mListItemBackgroundColorNormal;
-
- private int mListItemBackgroundColorPressed;
-
- private int mListItemBackgroundColorFocused;
-
- private final static int[] pressedState = {android.R.attr.state_pressed};
+ protected CharSequence getMessage() {
+ return getArguments().getCharSequence(ARG_MESSAGE);
+ }
- private final static int[] focusedState = {android.R.attr.state_focused};
+ /**
+ * Returns the title.
+ *
+ * @return title
+ */
+ protected String getTitle() {
+ return getArguments().getString(ARG_TITLE);
+ }
- private final static int[] defaultState = {android.R.attr.state_enabled};
+ /**
+ * Returns positive button text.
+ *
+ * @return text for the button.
+ */
+ protected String getPositiveButtonText() {
+ return getArguments().getString(ARG_POSITIVE_BUTTON);
+ }
- public Builder(DialogFragment dialogFragment, Context context, LayoutInflater inflater,
- ViewGroup container) {
- this.mDialogFragment = dialogFragment;
- this.mContext = context;
- this.mContainer = container;
- this.mInflater = inflater;
- }
+ /**
+ * Returns negative button text.
+ *
+ * @return text for the button.
+ */
+ protected String getNegativeButtonText() {
+ return getArguments().getString(ARG_NEGATIVE_BUTTON);
+ }
- public LayoutInflater getLayoutInflater() {
- return mInflater;
- }
+ /**
+ * Returns neutral button text.
+ *
+ * @return text for the button.
+ */
+ protected String getNeutralButtonText() {
+ return getArguments().getString(ARG_NEUTRAL_BUTTON);
+ }
- public Builder setTitle(int titleId) {
- this.mTitle = mContext.getText(titleId);
- return this;
- }
+ /**
+ * Gets arguments for list items.
+ *
+ * @return StringArray
+ */
+ protected String[] getItems() {
+ return getArguments().getStringArray(ARG_ITEMS);
+ }
- public Builder setTitle(CharSequence title) {
- this.mTitle = title;
- return this;
- }
+ /**
+ * Gets text array of multi choice list.
+ *
+ * @return array containing texts from multi choice list.
+ */
+ protected String[] getMultiChoiceItems() {
+ return getArguments().getStringArray(ARG_ITEMS_MULTICHOICE);
+ }
- public Builder setPositiveButton(int textId, final View.OnClickListener listener) {
- mPositiveButtonText = mContext.getText(textId);
- mPositiveButtonListener = listener;
- return this;
- }
+ /**
+ * Gets array with checked state of multi choice list.
+ *
+ * @return boolean array with checked states.
+ */
+ protected boolean[] getMultiChoiceSelectedItems() {
+ return getArguments().getBooleanArray(ARG_ITEMS_SELECTED_MULTICHOICE);
+ }
- public Builder setPositiveButton(CharSequence text, final View.OnClickListener listener) {
- mPositiveButtonText = text;
- mPositiveButtonListener = listener;
- return this;
- }
- public Builder setNegativeButton(int textId, final View.OnClickListener listener) {
- mNegativeButtonText = mContext.getText(textId);
- mNegativeButtonListener = listener;
- return this;
- }
-
- public Builder setNegativeButton(CharSequence text, final View.OnClickListener listener) {
- mNegativeButtonText = text;
- mNegativeButtonListener = listener;
- return this;
- }
-
- public Builder setNeutralButton(int textId, final View.OnClickListener listener) {
- mNeutralButtonText = mContext.getText(textId);
- mNeutralButtonListener = listener;
- return this;
- }
-
- public Builder setNeutralButton(CharSequence text, final View.OnClickListener listener) {
- mNeutralButtonText = text;
- mNeutralButtonListener = listener;
- return this;
- }
-
- public Builder setMessage(int messageId) {
- mMessage = mContext.getText(messageId);
- return this;
- }
-
- public Builder setMessage(CharSequence message) {
- mMessage = message;
- return this;
- }
-
- /**
- * Set list
- *
- * @param checkedItemIdx Item check by default, -1 if no item should be checked
- */
- public Builder setItems(ListAdapter listAdapter, int checkedItemIdx,
- final AdapterView.OnItemClickListener listener) {
- mListAdapter = listAdapter;
- mOnItemClickListener = listener;
- mListCheckedItemIdx = checkedItemIdx;
- return this;
- }
-
- public Builder setView(View view) {
- mView = view;
- mViewSpacingSpecified = false;
- return this;
- }
-
- public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop,
- int viewSpacingRight, int viewSpacingBottom) {
- mView = view;
- mViewSpacingSpecified = true;
- mViewSpacingLeft = viewSpacingLeft;
- mViewSpacingTop = viewSpacingTop;
- mViewSpacingRight = viewSpacingRight;
- mViewSpacingBottom = viewSpacingBottom;
- return this;
- }
-
- public Builder setIcon(int resourceId) {
- mIcon = mContext.getResources().getDrawable(resourceId);
- return this;
- }
-
- public Builder setIcon(Drawable drawable) {
- mIcon = drawable;
- return this;
- }
-
- public View create() {
- final Resources res = mContext.getResources();
- final int defaultTitleTextColor = res.getColor(R.color.sdl_title_text_dark);
- final int defaultTitleSeparatorColor = res.getColor(R.color.sdl_title_separator_dark);
- final int defaultMessageTextColor = res.getColor(R.color.sdl_message_text_dark);
- final ColorStateList defaultButtonTextColor = res
- .getColorStateList(R.color.sdl_button_text_dark);
- final int defaultButtonSeparatorColor = res.getColor(R.color.sdl_button_separator_dark);
- final int defaultButtonBackgroundColorNormal = res
- .getColor(R.color.sdl_button_normal_dark);
- final int defaultButtonBackgroundColorPressed = res
- .getColor(R.color.sdl_button_pressed_dark);
- final int defaultButtonBackgroundColorFocused = res
- .getColor(R.color.sdl_button_focused_dark);
-
- final TypedArray a = mContext.getTheme()
- .obtainStyledAttributes(null, R.styleable.DialogStyle, R.attr.sdlDialogStyle,
- 0);
- mTitleTextColor = a
- .getColor(R.styleable.DialogStyle_titleTextColor, defaultTitleTextColor);
- mTitleSeparatorColor = a.getColor(R.styleable.DialogStyle_titleSeparatorColor,
- defaultTitleSeparatorColor);
- mMessageTextColor = a
- .getColor(R.styleable.DialogStyle_messageTextColor, defaultMessageTextColor);
- mButtonTextColor = a.getColorStateList(R.styleable.DialogStyle_buttonTextColor);
- if (mButtonTextColor == null) {
- mButtonTextColor = defaultButtonTextColor;
- }
- mButtonSeparatorColor = a.getColor(R.styleable.DialogStyle_buttonSeparatorColor,
- defaultButtonSeparatorColor);
- mButtonBackgroundColorNormal = a
- .getColor(R.styleable.DialogStyle_buttonBackgroundColorNormal,
- defaultButtonBackgroundColorNormal);
- mButtonBackgroundColorPressed = a
- .getColor(R.styleable.DialogStyle_buttonBackgroundColorPressed,
- defaultButtonBackgroundColorPressed);
- mButtonBackgroundColorFocused = a
- .getColor(R.styleable.DialogStyle_buttonBackgroundColorFocused,
- defaultButtonBackgroundColorFocused);
- if (mListAdapter != null) {
- final int defaultListItemSeparatorColor = res
- .getColor(R.color.sdl_list_item_separator_dark);
- final int defaultListItemBackgroundColorNormal = res
- .getColor(R.color.sdl_button_normal_dark);
- final int defaultListItemBackgroundColorFocused = res
- .getColor(R.color.sdl_button_focused_dark);
- final int defaultListItemBackgroundColorPressed = res
- .getColor(R.color.sdl_button_pressed_dark);
- mListItemSeparatorColor = a.getColor(R.styleable.DialogStyle_listItemSeparatorColor,
- defaultListItemSeparatorColor);
- mListItemBackgroundColorNormal = a
- .getColor(R.styleable.DialogStyle_listItemColorNormal,
- defaultListItemBackgroundColorNormal);
- mListItemBackgroundColorFocused = a
- .getColor(R.styleable.DialogStyle_listItemColorFocused,
- defaultListItemBackgroundColorFocused);
- mListItemBackgroundColorPressed = a
- .getColor(R.styleable.DialogStyle_listItemColorPressed,
- defaultListItemBackgroundColorPressed);
- }
- a.recycle();
-
- View v = getDialogLayoutAndInitTitle();
-
- LinearLayout content = (LinearLayout) v.findViewById(R.id.sdl__content);
-
- if (mMessage != null) {
- View viewMessage = mInflater.inflate(R.layout.dialog_part_message, content, false);
- TextView tvMessage = (TextView) viewMessage.findViewById(R.id.sdl__message);
- tvMessage.setTextColor(mMessageTextColor);
- tvMessage.setText(mMessage);
- content.addView(viewMessage);
- }
-
- if (mView != null) {
- FrameLayout customPanel = (FrameLayout) mInflater
- .inflate(R.layout.dialog_part_custom, content, false);
- FrameLayout custom = (FrameLayout) customPanel.findViewById(R.id.sdl__custom);
- custom.addView(mView,
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT)
- );
- if (mViewSpacingSpecified) {
- custom.setPadding(mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
- mViewSpacingBottom);
- }
- content.addView(customPanel);
- }
-
- if (mListAdapter != null) {
- ListView listView = (ListView) mInflater
- .inflate(R.layout.dialog_part_list, content, false);
- listView.setAdapter(mListAdapter);
- listView.setDivider(getColoredListItemsDivider());
- listView.setDividerHeight(1);
- listView.setSelector(getListItemSelector());
- listView.setOnItemClickListener(mOnItemClickListener);
- if (mListCheckedItemIdx != -1) {
- listView.setSelection(mListCheckedItemIdx);
- }
- content.addView(listView);
- }
-
- addButtons(content);
-
- return v;
- }
-
- private View getDialogLayoutAndInitTitle() {
- View v = mInflater.inflate(R.layout.dialog_part_title, mContainer, false);
- TextView tvTitle = (TextView) v.findViewById(R.id.sdl__title);
- View viewTitleDivider = v.findViewById(R.id.sdl__titleDivider);
- if (mTitle != null) {
- tvTitle.setText(mTitle);
- tvTitle.setTextColor(mTitleTextColor);
- if (mIcon != null) {
- tvTitle.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- tvTitle.setCompoundDrawablePadding(
- mContext.getResources().getDimensionPixelSize(R.dimen.grid_2));
- }
- viewTitleDivider.setBackgroundDrawable(new ColorDrawable(mTitleSeparatorColor));
- } else {
- tvTitle.setVisibility(View.GONE);
- viewTitleDivider.setVisibility(View.GONE);
- }
- return v;
- }
-
- private void addButtons(LinearLayout llListDialog) {
- if (mNegativeButtonText != null || mNeutralButtonText != null
- || mPositiveButtonText != null) {
- View viewButtonPanel = mInflater
- .inflate(R.layout.dialog_part_button_panel, llListDialog, false);
- LinearLayout llButtonPanel = (LinearLayout) viewButtonPanel
- .findViewById(R.id.dialog_button_panel);
- viewButtonPanel.findViewById(R.id.dialog_horizontal_separator)
- .setBackgroundDrawable(new ColorDrawable(mButtonSeparatorColor));
-
- boolean addDivider = false;
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- addDivider = addPositiveButton(llButtonPanel, addDivider);
- } else {
- addDivider = addNegativeButton(llButtonPanel, addDivider);
- }
- addDivider = addNeutralButton(llButtonPanel, addDivider);
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- addNegativeButton(llButtonPanel, addDivider);
- } else {
- addPositiveButton(llButtonPanel, addDivider);
- }
-
- llListDialog.addView(viewButtonPanel);
- }
- }
-
- private boolean addNegativeButton(ViewGroup parent, boolean addDivider) {
- if (mNegativeButtonText != null) {
- if (addDivider) {
- addDivider(parent);
- }
- Button btn = (Button) mInflater.inflate(R.layout.dialog_part_button, parent, false);
- btn.setId(R.id.sdl__negative_button);
- btn.setText(mNegativeButtonText);
- btn.setTextColor(mButtonTextColor);
- btn.setBackgroundDrawable(getButtonBackground());
- btn.setOnClickListener(mNegativeButtonListener);
- parent.addView(btn);
- return true;
- }
- return addDivider;
- }
-
- private boolean addPositiveButton(ViewGroup parent, boolean addDivider) {
- if (mPositiveButtonText != null) {
- if (addDivider) {
- addDivider(parent);
- }
- Button btn = (Button) mInflater.inflate(R.layout.dialog_part_button, parent, false);
- btn.setId(R.id.sdl__positive_button);
- btn.setText(mPositiveButtonText);
- btn.setTextColor(mButtonTextColor);
- btn.setBackgroundDrawable(getButtonBackground());
- btn.setOnClickListener(mPositiveButtonListener);
- parent.addView(btn);
- return true;
- }
- return addDivider;
- }
-
- private boolean addNeutralButton(ViewGroup parent, boolean addDivider) {
- if (mNeutralButtonText != null) {
- if (addDivider) {
- addDivider(parent);
- }
- Button btn = (Button) mInflater.inflate(R.layout.dialog_part_button, parent, false);
- btn.setId(R.id.sdl__neutral_button);
- btn.setText(mNeutralButtonText);
- btn.setTextColor(mButtonTextColor);
- btn.setBackgroundDrawable(getButtonBackground());
- btn.setOnClickListener(mNeutralButtonListener);
- parent.addView(btn);
- return true;
- }
- return addDivider;
- }
-
- private void addDivider(ViewGroup parent) {
- View view = mInflater.inflate(R.layout.dialog_part_button_separator, parent, false);
- view.findViewById(R.id.dialog_button_separator)
- .setBackgroundDrawable(new ColorDrawable(mButtonSeparatorColor));
- parent.addView(view);
- }
-
- private StateListDrawable getButtonBackground() {
- ColorDrawable colorDefault = new ColorDrawable(mButtonBackgroundColorNormal);
- ColorDrawable colorPressed = new ColorDrawable(mButtonBackgroundColorPressed);
- ColorDrawable colorFocused = new ColorDrawable(mButtonBackgroundColorFocused);
- StateListDrawable background = new StateListDrawable();
- background.addState(pressedState, colorPressed);
- background.addState(focusedState, colorFocused);
- background.addState(defaultState, colorDefault);
- return background;
- }
-
- private StateListDrawable getListItemSelector() {
- ColorDrawable colorDefault = new ColorDrawable(mListItemBackgroundColorNormal);
- ColorDrawable colorPressed = new ColorDrawable(mListItemBackgroundColorPressed);
- ColorDrawable colorFocused = new ColorDrawable(mListItemBackgroundColorFocused);
- StateListDrawable background = new StateListDrawable();
- background.addState(pressedState, colorPressed);
- background.addState(focusedState, colorFocused);
- background.addState(defaultState, colorDefault);
- return background;
- }
-
- private ColorDrawable getColoredListItemsDivider() {
- ColorDrawable colorDividerDrawable = new ColorDrawable(mListItemSeparatorColor);
- return colorDividerDrawable;
- }
+ /**
+ * Custom dialog builder
+ */
+ protected static class Builder {
+ protected BaseDialogBuilder.DialogParams mDialogParams;
+
+
+ /**
+ * Public constructor.
+ *
+ * @param dialogFragment calling fragment
+ * @param context context
+ * @param inflater inflater
+ * @param container view group container
+ */
+ public Builder(DialogFragment dialogFragment, Context context, LayoutInflater inflater,
+ ViewGroup container) {
+ mDialogParams = new BaseDialogBuilder.DialogParams(context);
+ mDialogParams.dialogFragment = dialogFragment;
+ mDialogParams.container = container;
+ mDialogParams.layoutInflater = inflater;
+ }
+
+ /**
+ * Gets LayoutInflater.
+ *
+ * @return LayoutInflater
+ */
+ public LayoutInflater getLayoutInflater() {
+ return mDialogParams.layoutInflater;
+ }
+
+ /**
+ * Sets the dialog title.
+ *
+ * @param titleId resource id
+ * @return builder
+ */
+ public Builder setTitle(int titleId) {
+ mDialogParams.title = mDialogParams.context.getString(titleId);
+ return this;
+ }
+
+ /**
+ * Sets the dialog title.
+ *
+ * @param title dialog title
+ * @return builder
+ */
+ public Builder setTitle(CharSequence title) {
+ mDialogParams.title = title;
+ return this;
+ }
+
+ /**
+ * Sets dialog cancelable on touch outside.
+ *
+ * @param cancelableOnTouchOutside true/false (default is true)
+ * @return builder
+ */
+ public Builder setCancelableOnTouchOutside(boolean cancelableOnTouchOutside) {
+ mDialogParams.cancelableOnTouchOutside = cancelableOnTouchOutside;
+ return this;
+ }
+
+ /**
+ * Sets whether dialog can be cancelable.
+ *
+ * @param cancelable true/false (default is true)
+ * @return builder
+ */
+ public Builder setCancelable(boolean cancelable) {
+ mDialogParams.cancelable = cancelable;
+ return this;
+ }
+
+ /**
+ * Sets the positive button.
+ *
+ * @param textId button label
+ * @param listener button click listener
+ * @return builder
+ */
+ public Builder setPositiveButton(int textId, final View.OnClickListener listener) {
+ mDialogParams.positiveButtonText = mDialogParams.context.getString(textId);
+ mDialogParams.positiveButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets the positive button.
+ *
+ * @param text button label
+ * @param listener button click listener
+ * @return builder
+ */
+ public Builder setPositiveButton(CharSequence text, final View.OnClickListener listener) {
+ mDialogParams.positiveButtonText = text;
+ mDialogParams.positiveButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets the negative button.
+ *
+ * @param textId button label
+ * @param listener button click listener
+ * @return builder
+ */
+ public Builder setNegativeButton(int textId, final View.OnClickListener listener) {
+ mDialogParams.negativeButtonText = mDialogParams.context.getString(textId);
+ mDialogParams.negativeButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets the negative button.
+ *
+ * @param text button label
+ * @param listener button click listener
+ * @return builder
+ */
+ public Builder setNegativeButton(CharSequence text, final View.OnClickListener listener) {
+ mDialogParams.negativeButtonText = text;
+ mDialogParams.negativeButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets neutral button.
+ *
+ * @param textId button label
+ * @param listener button click listener
+ * @return builder
+ */
+ public Builder setNeutralButton(int textId, final View.OnClickListener listener) {
+ mDialogParams.neutralButtonText = mDialogParams.context.getString(textId);
+ mDialogParams.neutralButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets neutral button.
+ *
+ * @param text button label
+ * @param listener button click listener
+ * @return builder
+ */
+ public Builder setNeutralButton(CharSequence text, final View.OnClickListener listener) {
+ mDialogParams.neutralButtonText = text;
+ mDialogParams.neutralButtonListener = listener;
+ return this;
+ }
+
+ /**
+ * Sets the message for dialog.
+ *
+ * @param messageId main text in dialog
+ * @return builder
+ */
+ public Builder setMessage(int messageId) {
+ mDialogParams.message = mDialogParams.context.getString(messageId);
+ return this;
+ }
+
+ /**
+ * Sets the message for dialog.
+ *
+ * @param message main text in dialog
+ * @return builder
+ */
+ public Builder setMessage(CharSequence message) {
+ mDialogParams.message = message;
+ return this;
+ }
+
+ /**
+ * Sets list.
+ *
+ * @param listAdapter list adapter
+ * @param checkedItemIdx Item check by default, -1 if no item should be checked
+ * @param listener item click listener
+ * @return builder
+ */
+ public Builder setItems(ListAdapter listAdapter, int checkedItemIdx,
+ final AdapterView.OnItemClickListener listener) {
+ mDialogParams.listAdapter = listAdapter;
+ mDialogParams.onItemClickListener = listener;
+ mDialogParams.listCheckedItemIdx = checkedItemIdx;
+ return this;
+ }
+
+ /**
+ * Sets the list for multi choice.
+ *
+ * @param listAdapter The list adapter.
+ * @return builder
+ */
+ public Builder setMultiChoiceItems(ListAdapter listAdapter) {
+ mDialogParams.listAdapter = listAdapter;
+ return this;
+ }
+
+ /**
+ * Sets view for dialog.
+ *
+ * @param view main view
+ * @return builder
+ */
+ public Builder setView(View view) {
+ mDialogParams.view = view;
+ mDialogParams.viewSpacingSpecified = false;
+ return this;
+ }
+
+ /**
+ * Sets view for dialog.
+ *
+ * @param view main view
+ * @param viewSpacingLeft left padding
+ * @param viewSpacingTop top padding
+ * @param viewSpacingRight right padding
+ * @param viewSpacingBottom bottom padding
+ * @return builder
+ */
+ public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
+ int viewSpacingBottom) {
+ mDialogParams.view = view;
+ mDialogParams.viewSpacingSpecified = true;
+ mDialogParams.viewSpacingLeft = viewSpacingLeft;
+ mDialogParams.viewSpacingTop = viewSpacingTop;
+ mDialogParams.viewSpacingRight = viewSpacingRight;
+ mDialogParams.viewSpacingBottom = viewSpacingBottom;
+ return this;
+ }
+
+ /**
+ * Sets dialog icon.
+ *
+ * @param resourceId resource id of icon drawable
+ * @return builder
+ */
+ public Builder setIcon(int resourceId) {
+ mDialogParams.icon = mDialogParams.context.getResources().getDrawable(resourceId);
+ return this;
+ }
+
+ /**
+ * Sets dialog icon.
+ *
+ * @param drawable drawable
+ * @return builder
+ */
+ public Builder setIcon(Drawable drawable) {
+ mDialogParams.icon = drawable;
+ return this;
+ }
+
+ /**
+ * Creates dialog view.
+ *
+ * @return view
+ */
+ public View create() {
+ final Resources res = mDialogParams.context.getResources();
+ final int defaultTitleTextColor = res.getColor(R.color.sdl_title_text_dark);
+ final int defaultTitleSeparatorColor = res.getColor(R.color.sdl_title_separator_dark);
+ final int defaultMessageTextColor = res.getColor(R.color.sdl_message_text_dark);
+ final ColorStateList defaultButtonTextColor = res.getColorStateList(
+ R.color.sdl_button_text_dark);
+ final int defaultButtonSeparatorColor = res.getColor(R.color.sdl_button_separator_dark);
+ final int defaultButtonBackgroundColorNormal = res.getColor(R.color.sdl_button_normal_dark);
+ final int defaultButtonBackgroundColorPressed = res.getColor(R.color.sdl_button_pressed_dark);
+ final int defaultButtonBackgroundColorFocused = res.getColor(R.color.sdl_button_focused_dark);
+
+ final TypedArray a = mDialogParams.context.getTheme().obtainStyledAttributes(null,
+ R.styleable.DialogStyle, R.attr.sdlDialogStyle, 0);
+ mDialogParams.titleTextColor = a.getColor(R.styleable.DialogStyle_titleTextColor,
+ defaultTitleTextColor);
+ mDialogParams.titleSeparatorColor = a.getColor(R.styleable.DialogStyle_titleSeparatorColor,
+ defaultTitleSeparatorColor);
+ mDialogParams.messageTextColor = a.getColor(R.styleable.DialogStyle_messageTextColor,
+ defaultMessageTextColor);
+ mDialogParams.buttonTextColor = a.getColorStateList(R.styleable.DialogStyle_buttonTextColor);
+ if (mDialogParams.buttonTextColor == null) {
+ mDialogParams.buttonTextColor = defaultButtonTextColor;
+ }
+ mDialogParams.buttonSeparatorColor = a.getColor(R.styleable.DialogStyle_buttonSeparatorColor,
+ defaultButtonSeparatorColor);
+ mDialogParams.buttonBackgroundColorNormal = a.getColor(
+ R.styleable.DialogStyle_buttonBackgroundColorNormal, defaultButtonBackgroundColorNormal);
+ mDialogParams.buttonBackgroundColorPressed = a.getColor(
+ R.styleable.DialogStyle_buttonBackgroundColorPressed,
+ defaultButtonBackgroundColorPressed);
+ mDialogParams.buttonBackgroundColorFocused = a.getColor(
+ R.styleable.DialogStyle_buttonBackgroundColorFocused,
+ defaultButtonBackgroundColorFocused);
+
+ if (mDialogParams.listAdapter != null) {
+ final int defaultListItemSeparatorColor = res.getColor(
+ R.color.sdl_list_item_separator_dark);
+ final int defaultListItemBackgroundColorNormal = res.getColor(
+ R.color.sdl_button_normal_dark);
+ final int defaultListItemBackgroundColorFocused = res.getColor(
+ R.color.sdl_button_focused_dark);
+ final int defaultListItemBackgroundColorPressed = res.getColor(
+ R.color.sdl_button_pressed_dark);
+ mDialogParams.listItemSeparatorColor = a.getColor(
+ R.styleable.DialogStyle_listItemSeparatorColor, defaultListItemSeparatorColor);
+ mDialogParams.listItemBackgroundColorNormal = a.getColor(
+ R.styleable.DialogStyle_listItemColorNormal, defaultListItemBackgroundColorNormal);
+ mDialogParams.listItemBackgroundColorFocused = a.getColor(
+ R.styleable.DialogStyle_listItemColorFocused, defaultListItemBackgroundColorFocused);
+ mDialogParams.listItemBackgroundColorPressed = a.getColor(
+ R.styleable.DialogStyle_listItemColorPressed, defaultListItemBackgroundColorPressed);
+ }
+ a.recycle();
+
+ View view = getDialogLayoutAndInitTitle();
+
+ LinearLayout content = (LinearLayout)view.findViewById(R.id.sdl__content);
+
+ if (mDialogParams.message != null) {
+ View viewMessage = mDialogParams.layoutInflater.inflate(R.layout.dialog_part_message,
+ content, false);
+ TextView tvMessage = (TextView)viewMessage.findViewById(R.id.sdl__message);
+ tvMessage.setTextColor(mDialogParams.messageTextColor);
+ tvMessage.setText(mDialogParams.message);
+ content.addView(viewMessage);
+ }
+
+ if (mDialogParams.view != null) {
+ FrameLayout customPanel = (FrameLayout)mDialogParams.layoutInflater.inflate(
+ R.layout.dialog_part_custom, content, false);
+ FrameLayout custom = (FrameLayout)customPanel.findViewById(R.id.sdl__custom);
+ custom.addView(mDialogParams.view, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ if (mDialogParams.viewSpacingSpecified) {
+ custom.setPadding(mDialogParams.viewSpacingLeft, mDialogParams.viewSpacingTop,
+ mDialogParams.viewSpacingRight, mDialogParams.viewSpacingBottom);
+ }
+ content.addView(customPanel);
+ }
+
+ if (mDialogParams.listAdapter != null) {
+ ListView listView = (ListView)mDialogParams.layoutInflater.inflate(
+ R.layout.dialog_part_list, content, false);
+ listView.setAdapter(mDialogParams.listAdapter);
+ listView.setDivider(getColoredListItemsDivider());
+ listView.setDividerHeight(1);
+ listView.setSelector(getListItemSelector());
+ listView.setOnItemClickListener(mDialogParams.onItemClickListener);
+ if (mDialogParams.listCheckedItemIdx != -1) {
+ listView.setSelection(mDialogParams.listCheckedItemIdx);
+ }
+ content.addView(listView);
+ }
+
+ addButtons(content);
+
+ return view;
+ }
+
+ /**
+ * Gets dialog layout and inits title.
+ *
+ * @return view DialogLayout
+ */
+ @SuppressWarnings("deprecation")
+ private View getDialogLayoutAndInitTitle() {
+ View view = mDialogParams.layoutInflater.inflate(R.layout.dialog_part_title,
+ mDialogParams.container, false);
+ TextView tvTitle = (TextView)view.findViewById(R.id.sdl__title);
+ View viewTitleDivider = view.findViewById(R.id.sdl__titleDivider);
+
+ if (!TextUtils.isEmpty(mDialogParams.title)) {
+ tvTitle.setVisibility(View.VISIBLE);
+ viewTitleDivider.setVisibility(View.VISIBLE);
+ tvTitle.setText(mDialogParams.title);
+ tvTitle.setTextColor(mDialogParams.titleTextColor);
+ if (mDialogParams.icon != null) {
+ tvTitle.setCompoundDrawablesWithIntrinsicBounds(mDialogParams.icon, null, null, null);
+ tvTitle.setCompoundDrawablePadding(
+ mDialogParams.context.getResources().getDimensionPixelSize(R.dimen.grid_2));
+ }
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ viewTitleDivider.setBackgroundDrawable(new ColorDrawable(
+ mDialogParams.titleSeparatorColor));
+ } else {
+ viewTitleDivider.setBackground(new ColorDrawable(mDialogParams.titleSeparatorColor));
+ }
+
+ } else {
+ tvTitle.setVisibility(View.GONE);
+ viewTitleDivider.setVisibility(View.GONE);
+ }
+ return view;
+ }
+
+ /**
+ * Adds buttons to given layout.
+ *
+ * @param listDialog layout.
+ */
+ @SuppressWarnings("deprecation")
+ private void addButtons(LinearLayout listDialog) {
+ if (mDialogParams.negativeButtonText != null || mDialogParams.neutralButtonText != null ||
+ mDialogParams.positiveButtonText != null) {
+ View viewButtonPanel = mDialogParams.layoutInflater.inflate(
+ R.layout.dialog_part_button_panel, listDialog, false);
+ LinearLayout llButtonPanel = (LinearLayout)viewButtonPanel.findViewById(
+ R.id.dialog_button_panel);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ viewButtonPanel.findViewById(R.id.dialog_horizontal_separator).setBackgroundDrawable(
+ new ColorDrawable(mDialogParams.buttonSeparatorColor));
+ } else {
+ viewButtonPanel.findViewById(R.id.dialog_horizontal_separator).setBackground(
+ new ColorDrawable(mDialogParams.buttonSeparatorColor));
+ }
+
+ boolean addDivider = false;
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ addDivider = addPositiveButton(llButtonPanel, addDivider);
+ } else {
+ addDivider = addNegativeButton(llButtonPanel, addDivider);
+ }
+ addDivider = addNeutralButton(llButtonPanel, addDivider);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ addNegativeButton(llButtonPanel, addDivider);
+ } else {
+ addPositiveButton(llButtonPanel, addDivider);
+ }
+
+ listDialog.addView(viewButtonPanel);
+ }
+ }
+
+ /**
+ * Adds negative button.
+ *
+ * @param parent parent view group
+ * @param addDivider true/false whether parent needs divider
+ * @return divider defined in parent or true if negative button was added
+ */
+ @SuppressWarnings("deprecation")
+ private boolean addNegativeButton(ViewGroup parent, boolean addDivider) {
+ if (mDialogParams.negativeButtonText != null) {
+ if (addDivider) {
+ addDivider(parent);
+ }
+ Button btn = (Button)mDialogParams.layoutInflater.inflate(R.layout.dialog_part_button,
+ parent, false);
+ btn.setId(R.id.sdl__negative_button);
+ btn.setText(mDialogParams.negativeButtonText);
+ btn.setTextColor(mDialogParams.buttonTextColor);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ btn.setBackgroundDrawable(getButtonBackground());
+ } else {
+ btn.setBackground(getButtonBackground());
+ }
+
+ btn.setOnClickListener(mDialogParams.negativeButtonListener);
+ parent.addView(btn);
+ return true;
+ }
+ return addDivider;
+ }
+
+ /**
+ * Adds positive button.
+ *
+ * @param parent parent view group
+ * @param addDivider true/false whether parent needs divider
+ * @return divider defined in parent or true if positive button was added
+ */
+ @SuppressWarnings("deprecation")
+ private boolean addPositiveButton(ViewGroup parent, boolean addDivider) {
+ if (mDialogParams.positiveButtonText != null) {
+ if (addDivider) {
+ addDivider(parent);
+ }
+ Button btn = (Button)mDialogParams.layoutInflater.inflate(R.layout.dialog_part_button,
+ parent, false);
+ btn.setId(R.id.sdl__positive_button);
+ btn.setText(mDialogParams.positiveButtonText);
+ btn.setTextColor(mDialogParams.buttonTextColor);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ btn.setBackgroundDrawable(getButtonBackground());
+ } else {
+ btn.setBackground(getButtonBackground());
+ }
+ btn.setOnClickListener(mDialogParams.positiveButtonListener);
+ parent.addView(btn);
+ return true;
+ }
+ return addDivider;
+ }
+
+ /**
+ * Adds neutral button.
+ *
+ * @param parent parent view group
+ * @param addDivider true/false whether parent needs divider
+ * @return divider defined in parent or true if neutral button was added
+ */
+ @SuppressWarnings("deprecation")
+ private boolean addNeutralButton(ViewGroup parent, boolean addDivider) {
+ if (mDialogParams.neutralButtonText != null) {
+ if (addDivider) {
+ addDivider(parent);
+ }
+ Button btn = (Button)mDialogParams.layoutInflater.inflate(R.layout.dialog_part_button,
+ parent, false);
+ btn.setId(R.id.sdl__neutral_button);
+ btn.setText(mDialogParams.neutralButtonText);
+ btn.setTextColor(mDialogParams.buttonTextColor);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ btn.setBackgroundDrawable(getButtonBackground());
+ } else {
+ btn.setBackground(getButtonBackground());
+ }
+ btn.setOnClickListener(mDialogParams.neutralButtonListener);
+ parent.addView(btn);
+ return true;
+ }
+ return addDivider;
+ }
+
+ /**
+ * Adds divider.
+ *
+ * @param parent parent view group
+ */
+ @SuppressWarnings("deprecation")
+ private void addDivider(ViewGroup parent) {
+ View view = mDialogParams.layoutInflater.inflate(R.layout.dialog_part_button_separator,
+ parent, false);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ view.findViewById(R.id.dialog_button_separator).setBackgroundDrawable(new ColorDrawable(
+ mDialogParams.buttonSeparatorColor));
+ } else {
+ view.findViewById(R.id.dialog_button_separator).setBackground(new ColorDrawable(
+ mDialogParams.buttonSeparatorColor));
+ }
+ parent.addView(view);
+ }
+
+ /**
+ * Defines button background with all states.
+ *
+ * @return background for button.
+ */
+ private StateListDrawable getButtonBackground() {
+ ColorDrawable colorDefault = new ColorDrawable(mDialogParams.buttonBackgroundColorNormal);
+ ColorDrawable colorPressed = new ColorDrawable(mDialogParams.buttonBackgroundColorPressed);
+ ColorDrawable colorFocused = new ColorDrawable(mDialogParams.buttonBackgroundColorFocused);
+ StateListDrawable background = new StateListDrawable();
+ background.addState(pressedState, colorPressed);
+ background.addState(focusedState, colorFocused);
+ background.addState(defaultState, colorDefault);
+ return background;
+ }
+
+ /**
+ * Defines list item background with all states.
+ *
+ * @return background for list.
+ */
+ private StateListDrawable getListItemSelector() {
+ ColorDrawable colorDefault = new ColorDrawable(mDialogParams.listItemBackgroundColorNormal);
+ ColorDrawable colorPressed = new ColorDrawable(mDialogParams.listItemBackgroundColorPressed);
+ ColorDrawable colorFocused = new ColorDrawable(mDialogParams.listItemBackgroundColorFocused);
+ StateListDrawable background = new StateListDrawable();
+ background.addState(pressedState, colorPressed);
+ background.addState(focusedState, colorFocused);
+ background.addState(defaultState, colorDefault);
+ return background;
+ }
+
+ /**
+ * Defines list item divider.
+ *
+ * @return divider
+ */
+ private ColorDrawable getColoredListItemsDivider() {
+ ColorDrawable colorDividerDrawable = new ColorDrawable(mDialogParams.listItemSeparatorColor);
+ return colorDividerDrawable;
+ }
}
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/DatePickerDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/DatePickerDialogFragment.java
index 762afe8..1bad017 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/DatePickerDialogFragment.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/DatePickerDialogFragment.java
@@ -1,6 +1,11 @@
package eu.inmite.android.lib.dialogs;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -9,19 +14,12 @@
import android.view.View;
import android.widget.DatePicker;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.TimeZone;
-
/**
* Dialog with a date picker. Implement {@link eu.inmite.android.lib.dialogs.IDateDialogListener} or {@link eu.inmite.android.lib.dialogs.IDateDialogCancelListener} to handle events.
*/
public class DatePickerDialogFragment extends BaseDialogFragment {
protected static final String ARG_ZONE = "zone";
- protected static final String ARG_TITLE = "title";
- protected static final String ARG_POSITIVE_BUTTON = "positive_button";
- protected static final String ARG_NEGATIVE_BUTTON = "negative_button";
protected static final String ARG_DATE = "date";
protected static final String ARG_24H = "24h";
@@ -44,7 +42,7 @@ public void onActivityCreated(Bundle savedInstanceState) {
} else {
Bundle args = getArguments();
if (args != null) {
- mRequestCode = args.getInt(BaseDialogBuilder.ARG_REQUEST_CODE, 0);
+ mRequestCode = args.getInt(BaseDialogFragment.ARG_REQUEST_CODE, 0);
}
}
}
@@ -53,11 +51,11 @@ protected IDateDialogListener getDialogListener() {
final Fragment targetFragment = getTargetFragment();
if (targetFragment != null) {
if (targetFragment instanceof IDateDialogListener) {
- return (IDateDialogListener) targetFragment;
+ return (IDateDialogListener)targetFragment;
}
} else {
if (getActivity() instanceof IDateDialogListener) {
- return (IDateDialogListener) getActivity();
+ return (IDateDialogListener)getActivity();
}
}
return null;
@@ -67,16 +65,25 @@ protected IDateDialogCancelListener getCancelListener() {
final Fragment targetFragment = getTargetFragment();
if (targetFragment != null) {
if (targetFragment instanceof IDateDialogCancelListener) {
- return (IDateDialogCancelListener) targetFragment;
+ return (IDateDialogCancelListener)targetFragment;
}
} else {
if (getActivity() instanceof IDateDialogCancelListener) {
- return (IDateDialogCancelListener) getActivity();
+ return (IDateDialogCancelListener)getActivity();
}
}
return null;
}
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ IDateDialogCancelListener onDateDialogCanceled = getCancelListener();
+ if (onDateDialogCanceled != null) {
+ onDateDialogCanceled.onCancelled(mRequestCode, getDate());
+ }
+ }
+
@Override
protected BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
final String title = getTitle();
@@ -113,30 +120,18 @@ public void onClick(View view) {
}
});
}
- mDatePicker = (DatePicker) LayoutInflater.from(getActivity()).inflate(R.layout.dialog_part_datepicker, null);
+ mDatePicker = (DatePicker)LayoutInflater.from(getActivity()).inflate(R.layout.dialog_part_datepicker, null);
builder.setView(mDatePicker);
TimeZone zone = TimeZone.getTimeZone(getArguments().getString(ARG_ZONE));
mCalendar = Calendar.getInstance(zone);
mCalendar.setTimeInMillis(getArguments().getLong(ARG_DATE, System.currentTimeMillis()));
mDatePicker.updateDate(mCalendar.get(Calendar.YEAR)
- , mCalendar.get(Calendar.MONTH)
- , mCalendar.get(Calendar.DAY_OF_MONTH));
+ , mCalendar.get(Calendar.MONTH)
+ , mCalendar.get(Calendar.DAY_OF_MONTH));
return builder;
}
- protected String getTitle() {
- return getArguments().getString(ARG_TITLE);
- }
-
- protected String getPositiveButtonText() {
- return getArguments().getString(ARG_POSITIVE_BUTTON);
- }
-
- protected String getNegativeButtonText() {
- return getArguments().getString(ARG_NEGATIVE_BUTTON);
- }
-
public Date getDate() {
mCalendar.set(Calendar.YEAR, mDatePicker.getYear());
mCalendar.set(Calendar.MONTH, mDatePicker.getMonth());
@@ -148,10 +143,6 @@ public static class SimpleDialogBuilder extends BaseDialogBuilder
- * Default is true.
- */
- public SimpleDialogBuilder hideDefaultButton(boolean hide) {
- mShowDefaultButton = !hide;
- return this;
- }
-
-
@Override
protected Bundle prepareArguments() {
- if (mShowDefaultButton && mPositiveButtonText == null && mNegativeButtonText == null) {
- mPositiveButtonText = mContext.getString(R.string.dialog_close);
+ if (mShowDefaultButton && mDialogParams.positiveButtonText == null && mDialogParams.negativeButtonText
+ == null) {
+ mDialogParams.positiveButtonText = mDialogParams.context.getString(R.string.dialog_close);
}
Bundle args = new Bundle();
- args.putString(SimpleDialogFragment.ARG_TITLE, mTitle);
- args.putString(SimpleDialogFragment.ARG_POSITIVE_BUTTON, mPositiveButtonText);
- args.putString(SimpleDialogFragment.ARG_NEGATIVE_BUTTON, mNegativeButtonText);
+ if (!TextUtils.isEmpty(mDialogParams.title)) {
+ args.putString(BaseDialogFragment.ARG_TITLE, mDialogParams.title.toString());
+ }
+ if (mDialogParams.positiveButtonText != null) {
+ args.putString(BaseDialogFragment.ARG_POSITIVE_BUTTON, mDialogParams.positiveButtonText
+ .toString());
+ }
+ if (mDialogParams.negativeButtonText != null) {
+ args.putString(BaseDialogFragment.ARG_NEGATIVE_BUTTON,
+ mDialogParams.negativeButtonText.toString());
+ }
args.putLong(ARG_DATE, mDate.getTime());
args.putBoolean(ARG_24H, m24h);
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogCancelListener.java b/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogCancelListener.java
index 4828f82..1e4be1e 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogCancelListener.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogCancelListener.java
@@ -23,5 +23,5 @@
* @since 13/05/2013
*/
public interface IDateDialogCancelListener {
- public void onCancelled(int requestCode, Date date);
+ public void onCancelled(int requestCode, Date date);
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogListener.java b/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogListener.java
index f4cee37..f5c094f 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogListener.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/IDateDialogListener.java
@@ -24,7 +24,7 @@
* @author David Vávra (david@inmite.eu)
*/
public interface IDateDialogListener {
- public void onPositiveButtonClicked(int requestCode, Date date);
+ public void onPositiveButtonClicked(int requestCode, Date date);
- public void onNegativeButtonClicked(int requestCode, Date date);
+ public void onNegativeButtonClicked(int requestCode, Date date);
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/IListDialogListener.java b/library/src/main/java/eu/inmite/android/lib/dialogs/IListDialogListener.java
index 10c8c85..03790b7 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/IListDialogListener.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/IListDialogListener.java
@@ -6,5 +6,6 @@
public interface IListDialogListener {
public void onListItemSelected(String value, int number);
+
public void onCancelled();
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/INumberDialogListener.java b/library/src/main/java/eu/inmite/android/lib/dialogs/INumberDialogListener.java
new file mode 100644
index 0000000..b24e392
--- /dev/null
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/INumberDialogListener.java
@@ -0,0 +1,25 @@
+package eu.inmite.android.lib.dialogs;
+
+/**
+ * Implement this interface in Activity or Fragment to react to positive and negative buttons.
+ *
+ * @author Lukas Prokop (prokop@avast.com)
+ */
+public interface INumberDialogListener {
+
+ /**
+ * Callback fired on positive button clicked
+ *
+ * @param requestCode identifier of caller
+ * @param number from number picker
+ */
+ public void onPositiveButtonClicked(int requestCode, int number);
+
+ /**
+ * Callback fired on positive button clicked
+ *
+ * @param requestCode identifier of caller
+ * @param number from number picker
+ */
+ public void onNegativeButtonClicked(int requestCode, int number);
+}
\ No newline at end of file
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogCancelListener.java b/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogCancelListener.java
index 896040e..dcfc1cc 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogCancelListener.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogCancelListener.java
@@ -17,9 +17,15 @@
package eu.inmite.android.lib.dialogs;
/**
- * @author Tomáš Kypta
- * @since 13/05/2013
+ * Implement this interface in Activity or Fragment to react to cancel event.
+ *
+ * @author Tomáš Kypta (inmite.eu)
*/
public interface ISimpleDialogCancelListener {
- public void onCancelled(int requestCode);
+ /**
+ * Callback to react on cancel event.
+ *
+ * @param requestCode identifies dialogue
+ */
+ public void onCancelled(int requestCode);
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogListener.java b/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogListener.java
index 8ef36a4..ac462b1 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogListener.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/ISimpleDialogListener.java
@@ -17,14 +17,30 @@
package eu.inmite.android.lib.dialogs;
/**
- * Implement this interface in Activity or Fragment to react to positive and negative buttons.
+ * Implement this interface in Activity or Fragment to react to positive, negative and neutral
+ * buttons.
*
* @author David Vávra (david@inmite.eu)
*/
public interface ISimpleDialogListener {
- public void onPositiveButtonClicked(int requestCode);
+ /**
+ * Callback for click on positive button.
+ *
+ * @param requestCode enables to identify dialog
+ */
+ public void onPositiveButtonClicked(int requestCode);
- public void onNegativeButtonClicked(int requestCode);
+ /**
+ * Callback for click on negative button.
+ *
+ * @param requestCode enables to identify dialog
+ */
+ public void onNegativeButtonClicked(int requestCode);
- public void onNeutralButtonClicked(int requestCode);
+ /**
+ * Callback for click on neutral button.
+ *
+ * @param requestCode enables to identify dialog
+ */
+ public void onNeutralButtonClicked(int requestCode);
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/ListDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/ListDialogFragment.java
index 36ba208..d796e44 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/ListDialogFragment.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/ListDialogFragment.java
@@ -17,8 +17,6 @@
*/
public class ListDialogFragment extends BaseDialogFragment {
- private static String ARG_ITEMS = "items";
-
public static SimpleListDialogBuilder createBuilder(Context context,
FragmentManager fragmentManager) {
return new SimpleListDialogBuilder(context, fragmentManager);
@@ -29,7 +27,7 @@ public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments() == null) {
throw new IllegalArgumentException(
- "use SimpleListDialogBuilder to construct this dialog");
+ "use SimpleListDialogBuilder to construct this dialog");
}
}
@@ -53,17 +51,7 @@ protected SimpleListDialogBuilder self() {
}
private Resources getResources() {
- return mContext.getResources();
- }
-
- public SimpleListDialogBuilder setTitle(String title) {
- this.title = title;
- return this;
- }
-
- public SimpleListDialogBuilder setTitle(int titleResID) {
- this.title = getResources().getString(titleResID);
- return this;
+ return mDialogParams.context.getResources();
}
public SimpleListDialogBuilder setItems(String[] items) {
@@ -88,29 +76,19 @@ public SimpleListDialogBuilder setCancelButtonText(int cancelBttTextResID) {
@Override
public ListDialogFragment show() {
- return (ListDialogFragment) super.show();
- }
-
- /**
- * When there is neither positive nor negative button, default "close" button is created if
- * it was enabled.
- * Default is true.
- */
- public SimpleListDialogBuilder hideDefaultButton(boolean hide) {
- mShowDefaultButton = !hide;
- return this;
+ return (ListDialogFragment)super.show();
}
@Override
protected Bundle prepareArguments() {
if (mShowDefaultButton && cancelButtonText == null) {
- cancelButtonText = mContext.getString(R.string.dialog_close);
+ cancelButtonText = mDialogParams.context.getString(R.string.dialog_close);
}
Bundle args = new Bundle();
- args.putString(SimpleDialogFragment.ARG_TITLE, title);
- args.putString(SimpleDialogFragment.ARG_POSITIVE_BUTTON, cancelButtonText);
- args.putStringArray(ARG_ITEMS, items);
+ args.putString(BaseDialogFragment.ARG_TITLE, title);
+ args.putString(BaseDialogFragment.ARG_POSITIVE_BUTTON, cancelButtonText);
+ args.putStringArray(BaseDialogFragment.ARG_ITEMS, items);
return args;
}
@@ -148,9 +126,9 @@ public void onClick(View view) {
final String[] items = getItems();
if (items != null && items.length > 0) {
ListAdapter adapter = new ArrayAdapter(getActivity(),
- R.layout.dialog_list_item,
- R.id.list_item_text,
- items);
+ R.layout.dialog_list_item,
+ R.id.list_item_text,
+ items);
builder.setItems(adapter, 0, new AdapterView.OnItemClickListener() {
@Override
@@ -158,7 +136,7 @@ public void onItemClick(AdapterView> parent, View view, int position, long id)
IListDialogListener onListItemSelectedListener = getDialogListener();
if (onListItemSelectedListener != null) {
onListItemSelectedListener
- .onListItemSelected(getItems()[position], position);
+ .onListItemSelected(getItems()[position], position);
dismiss();
}
}
@@ -172,26 +150,13 @@ private IListDialogListener getDialogListener() {
final Fragment targetFragment = getTargetFragment();
if (targetFragment != null) {
if (targetFragment instanceof IListDialogListener) {
- return (IListDialogListener) targetFragment;
+ return (IListDialogListener)targetFragment;
}
} else {
if (getActivity() instanceof IListDialogListener) {
- return (IListDialogListener) getActivity();
+ return (IListDialogListener)getActivity();
}
}
return null;
}
-
- private String getTitle() {
- return getArguments().getString(SimpleDialogFragment.ARG_TITLE);
- }
-
- private String[] getItems() {
- return getArguments().getStringArray(ARG_ITEMS);
- }
-
- private String getPositiveButtonText() {
- return getArguments().getString(SimpleDialogFragment.ARG_POSITIVE_BUTTON);
- }
-
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/NumberPickerDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/NumberPickerDialogFragment.java
new file mode 100644
index 0000000..fdc2113
--- /dev/null
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/NumberPickerDialogFragment.java
@@ -0,0 +1,179 @@
+package eu.inmite.android.lib.dialogs;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.numberpicker.backport.NumberPicker;
+
+/**
+ /**
+ * Simple implementation of styled number picker dialog.
+ *
+ * Implement {@link eu.inmite.android.lib.dialogs.INumberDialogListener} in your Fragment or Activity to react on positive,
+ * negative and/or neutral button clicks. This class can be extended and more parameters can be
+ * added in overridden build() method.
+ *
+ * @author kd7uiy (https://github.com/kd7uiy/android-styled-dialogs)
+ * @author Lukas Prokop (prokop@avast.com)
+ */
+public class NumberPickerDialogFragment extends BaseDialogFragment {
+
+ protected static final String ARG_NUMBER = "number";
+ protected static final String ARG_MIN = "minValue";
+ protected static final String ARG_MAX = "maxValue";
+
+ private NumberPicker mNumberPicker;
+ private int mNumber;
+ private int mMinValue;
+ private int mMaxValue;
+
+ public static NumberPickerDialogBuilder createBuilder(Context context,
+ FragmentManager fragmentManager) {
+ return new NumberPickerDialogBuilder(context, fragmentManager, NumberPickerDialogFragment.class);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null) {
+ mRequestCode = getTargetRequestCode();
+ } else {
+ Bundle args = getArguments();
+ if (args != null) {
+ mRequestCode = args.getInt(BaseDialogFragment.ARG_REQUEST_CODE, 0);
+ }
+ }
+ }
+
+ protected INumberDialogListener getDialogListener() {
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null) {
+ if (targetFragment instanceof INumberDialogListener) {
+ return (INumberDialogListener) targetFragment;
+ }
+ } else {
+ if (getActivity() instanceof INumberDialogListener) {
+ return (INumberDialogListener) getActivity();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
+ final String title = getTitle();
+ if (!TextUtils.isEmpty(title)) {
+ builder.setTitle(title);
+ }
+
+ final String positiveButtonText = getPositiveButtonText();
+ if (!TextUtils.isEmpty(positiveButtonText)) {
+ builder.setPositiveButton(positiveButtonText, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ INumberDialogListener listener = getDialogListener();
+ if (listener != null) {
+ listener.onPositiveButtonClicked(mRequestCode, getNumber());
+ }
+ dismiss();
+ }
+ });
+ }
+
+ final String negativeButtonText = getNegativeButtonText();
+ if (!TextUtils.isEmpty(negativeButtonText)) {
+ builder.setNegativeButton(negativeButtonText, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ INumberDialogListener listener = getDialogListener();
+ if (listener != null) {
+ listener.onNegativeButtonClicked(mRequestCode, getNumber());
+ }
+ dismiss();
+ }
+ });
+ }
+
+ mNumberPicker = (NumberPicker) LayoutInflater.from(getActivity()).inflate(
+ R.layout.dialog_part_numberpicker, null);
+ builder.setView(mNumberPicker);
+
+ mMinValue = getArguments().getInt(ARG_MIN, Integer.MIN_VALUE);
+ mMaxValue = getArguments().getInt(ARG_MAX, Integer.MAX_VALUE);
+ mNumber = getArguments().getInt(ARG_NUMBER, mMinValue);
+
+ mNumberPicker.setMinValue(mMinValue);
+ mNumberPicker.setMaxValue(mMaxValue);
+ mNumberPicker.setValue(mNumber);
+
+ return builder;
+ }
+
+ public int getNumber() {
+ return mNumberPicker.getValue();
+ }
+
+ /**
+ * Custom builder
+ */
+ public static class NumberPickerDialogBuilder
+ extends BaseDialogBuilder {
+ int mNumber = 0;
+ private int mMin = Integer.MIN_VALUE;
+ private int mMax = Integer.MAX_VALUE;
+
+ protected NumberPickerDialogBuilder(Context context, FragmentManager fragmentManager,
+ Class extends NumberPickerDialogFragment> clazz) {
+ super(context, fragmentManager, clazz);
+ }
+
+ public NumberPickerDialogBuilder setNumber(int number) {
+ mNumber = number;
+ return this;
+ }
+
+ public NumberPickerDialogBuilder setMinValue(int minValue) {
+ mMin = minValue;
+ return this;
+ }
+
+ public NumberPickerDialogBuilder setMaxValue(int maxValue) {
+ mMax = maxValue;
+ return this;
+ }
+
+ @Override
+ protected Bundle prepareArguments() {
+ if (mDialogParams.showDefaultButton && mDialogParams.positiveButtonText == null &&
+ mDialogParams.negativeButtonText == null) {
+ mDialogParams.positiveButtonText = mDialogParams.context.getString(R.string.dialog_close);
+ }
+
+ Bundle args = new Bundle();
+ if (!TextUtils.isEmpty(mDialogParams.title)) {
+ args.putString(BaseDialogFragment.ARG_TITLE, mDialogParams.title.toString());
+ }
+ args.putString(BaseDialogFragment.ARG_POSITIVE_BUTTON,
+ mDialogParams.positiveButtonText.toString());
+ if (!TextUtils.isEmpty(mDialogParams.negativeButtonText)) {
+ args.putString(BaseDialogFragment.ARG_NEGATIVE_BUTTON,
+ mDialogParams.negativeButtonText.toString());
+ }
+ args.putInt(ARG_MIN, mMin);
+ args.putInt(ARG_MAX, mMax);
+ args.putInt(ARG_NUMBER, mNumber);
+
+ return args;
+ }
+
+ @Override
+ protected NumberPickerDialogBuilder self() {
+ return this;
+ }
+ }
+}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/ProgressDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/ProgressDialogFragment.java
index f509043..af7c5ea 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/ProgressDialogFragment.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/ProgressDialogFragment.java
@@ -6,6 +6,7 @@
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
@@ -23,111 +24,86 @@
*/
public class ProgressDialogFragment extends BaseDialogFragment {
- protected final static String ARG_MESSAGE = "message";
- protected final static String ARG_TITLE = "title";
-
- protected int mRequestCode;
-
- public static ProgressDialogBuilder createBuilder(Context context, FragmentManager fragmentManager) {
- return new ProgressDialogBuilder(context, fragmentManager);
- }
-
- @Override
- protected Builder build(Builder builder) {
- final int defaultMessageTextColor = getResources().getColor(R.color.sdl_message_text_dark);
- final TypedArray a = getActivity().getTheme().obtainStyledAttributes(null, R.styleable.DialogStyle, R.attr.sdlDialogStyle, 0);
- final int messageTextColor = a.getColor(R.styleable.DialogStyle_messageTextColor, defaultMessageTextColor);
- a.recycle();
-
- final LayoutInflater inflater = builder.getLayoutInflater();
- final View view = inflater.inflate(R.layout.dialog_part_progress, null, false);
- final TextView tvMessage = (TextView) view.findViewById(R.id.sdl__message);
- tvMessage.setText(getArguments().getString(ARG_MESSAGE));
- tvMessage.setTextColor(messageTextColor);
-
- builder.setView(view);
-
- builder.setTitle(getArguments().getString(ARG_TITLE));
-
- return builder;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (getArguments() == null) {
- throw new IllegalArgumentException("use ProgressDialogBuilder to construct this dialog");
- }
- final Fragment targetFragment = getTargetFragment();
- mRequestCode = targetFragment != null ?
- getTargetRequestCode() : getArguments().getInt(BaseDialogBuilder.ARG_REQUEST_CODE, 0);
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- super.onCancel(dialog);
- ISimpleDialogCancelListener listener = getCancelListener();
- if (listener != null) {
- listener.onCancelled(mRequestCode);
- }
- }
-
- protected ISimpleDialogCancelListener getCancelListener() {
- final Fragment targetFragment = getTargetFragment();
- if (targetFragment != null) {
- if (targetFragment instanceof ISimpleDialogCancelListener) {
- return (ISimpleDialogCancelListener) targetFragment;
- }
- } else {
- if (getActivity() instanceof ISimpleDialogCancelListener) {
- return (ISimpleDialogCancelListener) getActivity();
- }
- }
- return null;
- }
-
- public static class ProgressDialogBuilder extends BaseDialogBuilder {
-
- private String mTitle;
- private String mMessage;
-
- protected ProgressDialogBuilder(Context context, FragmentManager fragmentManager) {
- super(context, fragmentManager, ProgressDialogFragment.class);
- }
-
- @Override
- protected ProgressDialogBuilder self() {
- return this;
- }
-
- public ProgressDialogBuilder setTitle(int titleResourceId) {
- mTitle = mContext.getString(titleResourceId);
- return this;
- }
-
-
- public ProgressDialogBuilder setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- public ProgressDialogBuilder setMessage(int messageResourceId) {
- mMessage = mContext.getString(messageResourceId);
- return this;
- }
-
- public ProgressDialogBuilder setMessage(String message) {
- mMessage = message;
- return this;
- }
-
- @Override
- protected Bundle prepareArguments() {
- Bundle args = new Bundle();
- args.putString(SimpleDialogFragment.ARG_MESSAGE, mMessage);
- args.putString(SimpleDialogFragment.ARG_TITLE, mTitle);
-
- return args;
- }
- }
+ public static ProgressDialogBuilder createBuilder(Context context,
+ FragmentManager fragmentManager) {
+ return new ProgressDialogBuilder(context, fragmentManager);
+ }
+
+ @Override
+ protected Builder build(Builder builder) {
+ final int defaultMessageTextColor = getResources().getColor(R.color.sdl_message_text_dark);
+ final TypedArray a = getActivity().getTheme().obtainStyledAttributes(null,
+ R.styleable.DialogStyle, R.attr.sdlDialogStyle, 0);
+ final int messageTextColor = a.getColor(R.styleable.DialogStyle_messageTextColor,
+ defaultMessageTextColor);
+ a.recycle();
+
+ final LayoutInflater inflater = builder.getLayoutInflater();
+ final View view = inflater.inflate(R.layout.dialog_part_progress, null, false);
+ final TextView tvMessage = (TextView)view.findViewById(R.id.sdl__message);
+ tvMessage.setText(getArguments().getString(BaseDialogFragment.ARG_MESSAGE));
+ tvMessage.setTextColor(messageTextColor);
+
+ builder.setView(view);
+
+ builder.setTitle(getArguments().getString(BaseDialogFragment.ARG_TITLE));
+
+ return builder;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (getArguments() == null) {
+ throw new IllegalArgumentException("use ProgressDialogBuilder to construct this dialog");
+ }
+ final Fragment targetFragment = getTargetFragment();
+ mRequestCode = targetFragment != null ? getTargetRequestCode() : getArguments().getInt(
+ BaseDialogFragment.ARG_REQUEST_CODE, 0);
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ ISimpleDialogCancelListener listener = getCancelListener();
+ if (listener != null) {
+ listener.onCancelled(mRequestCode);
+ }
+ }
+
+ protected ISimpleDialogCancelListener getCancelListener() {
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null) {
+ if (targetFragment instanceof ISimpleDialogCancelListener) {
+ return (ISimpleDialogCancelListener)targetFragment;
+ }
+ } else {
+ if (getActivity() instanceof ISimpleDialogCancelListener) {
+ return (ISimpleDialogCancelListener)getActivity();
+ }
+ }
+ return null;
+ }
+
+ public static class ProgressDialogBuilder extends BaseDialogBuilder {
+
+ protected ProgressDialogBuilder(Context context, FragmentManager fragmentManager) {
+ super(context, fragmentManager, ProgressDialogFragment.class);
+ }
+
+ @Override
+ protected ProgressDialogBuilder self() {
+ return this;
+ }
+
+ @Override
+ protected Bundle prepareArguments() {
+ Bundle args = new Bundle();
+ args.putString(BaseDialogFragment.ARG_MESSAGE, mDialogParams.message.toString());
+ if (!TextUtils.isEmpty(mDialogParams.title)) {
+ args.putString(BaseDialogFragment.ARG_TITLE, mDialogParams.title.toString());
+ }
+ return args;
+ }
+ }
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/SimpleDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/SimpleDialogFragment.java
index 1536dda..4211f96 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/SimpleDialogFragment.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/SimpleDialogFragment.java
@@ -21,8 +21,6 @@
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
-import android.text.Html;
-import android.text.SpannedString;
import android.text.TextUtils;
import android.view.View;
@@ -35,251 +33,160 @@
*/
public class SimpleDialogFragment extends BaseDialogFragment {
- protected final static String ARG_MESSAGE = "message";
- protected final static String ARG_TITLE = "title";
- protected final static String ARG_POSITIVE_BUTTON = "positive_button";
- protected final static String ARG_NEGATIVE_BUTTON = "negative_button";
- protected final static String ARG_NEUTRAL_BUTTON = "neutral_button";
-
- protected int mRequestCode;
-
- public static SimpleDialogBuilder createBuilder(Context context, FragmentManager fragmentManager) {
- return new SimpleDialogBuilder(context, fragmentManager, SimpleDialogFragment.class);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- final Fragment targetFragment = getTargetFragment();
- if (targetFragment != null) {
- mRequestCode = getTargetRequestCode();
- } else {
- Bundle args = getArguments();
- if (args != null) {
- mRequestCode = args.getInt(BaseDialogBuilder.ARG_REQUEST_CODE, 0);
- }
- }
- }
-
- /**
- * Children can extend this to add more things to base builder.
- */
- @Override
- protected BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
- final String title = getTitle();
- if (!TextUtils.isEmpty(title)) {
- builder.setTitle(title);
- }
-
- final CharSequence message = getMessage();
- if (!TextUtils.isEmpty(message)) {
- builder.setMessage(message);
- }
-
- final String positiveButtonText = getPositiveButtonText();
- if (!TextUtils.isEmpty(positiveButtonText)) {
- builder.setPositiveButton(positiveButtonText, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ISimpleDialogListener listener = getDialogListener();
- if (listener != null) {
- listener.onPositiveButtonClicked(mRequestCode);
- }
- dismiss();
- }
- });
- }
-
- final String negativeButtonText = getNegativeButtonText();
- if (!TextUtils.isEmpty(negativeButtonText)) {
- builder.setNegativeButton(negativeButtonText, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ISimpleDialogListener listener = getDialogListener();
- if (listener != null) {
- listener.onNegativeButtonClicked(mRequestCode);
- }
- dismiss();
- }
- });
- }
-
- final String neutralButtonText = getNeutralButtonText();
- if (!TextUtils.isEmpty(neutralButtonText)) {
- builder.setNeutralButton(neutralButtonText, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ISimpleDialogListener listener = getDialogListener();
- if (listener != null) {
- listener.onNeutralButtonClicked(mRequestCode);
- }
- dismiss();
- }
- });
- }
-
- return builder;
- }
-
- protected CharSequence getMessage() {
- return getArguments().getCharSequence(ARG_MESSAGE);
- }
-
- protected String getTitle() {
- return getArguments().getString(ARG_TITLE);
- }
-
- protected String getPositiveButtonText() {
- return getArguments().getString(ARG_POSITIVE_BUTTON);
- }
-
- protected String getNegativeButtonText() {
- return getArguments().getString(ARG_NEGATIVE_BUTTON);
- }
-
- protected String getNeutralButtonText() {
- return getArguments().getString(ARG_NEUTRAL_BUTTON);
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- super.onCancel(dialog);
- ISimpleDialogCancelListener listener = getCancelListener();
- if (listener != null) {
- listener.onCancelled(mRequestCode);
- }
- }
-
- protected ISimpleDialogListener getDialogListener() {
- final Fragment targetFragment = getTargetFragment();
- if (targetFragment != null) {
- if (targetFragment instanceof ISimpleDialogListener) {
- return (ISimpleDialogListener) targetFragment;
- }
- } else {
- if (getActivity() instanceof ISimpleDialogListener) {
- return (ISimpleDialogListener) getActivity();
- }
- }
- return null;
- }
-
- protected ISimpleDialogCancelListener getCancelListener() {
- final Fragment targetFragment = getTargetFragment();
- if (targetFragment != null) {
- if (targetFragment instanceof ISimpleDialogCancelListener) {
- return (ISimpleDialogCancelListener) targetFragment;
- }
- } else {
- if (getActivity() instanceof ISimpleDialogCancelListener) {
- return (ISimpleDialogCancelListener) getActivity();
- }
- }
- return null;
- }
-
- public static class SimpleDialogBuilder extends BaseDialogBuilder {
-
- private String mTitle;
- private CharSequence mMessage;
- private String mPositiveButtonText;
- private String mNegativeButtonText;
- private String mNeutralButtonText;
-
- private boolean mShowDefaultButton = true;
-
- protected SimpleDialogBuilder(Context context, FragmentManager fragmentManager, Class extends SimpleDialogFragment> clazz) {
- super(context, fragmentManager, clazz);
- }
-
- @Override
- protected SimpleDialogBuilder self() {
- return this;
- }
-
- public SimpleDialogBuilder setTitle(int titleResourceId) {
- mTitle = mContext.getString(titleResourceId);
- return this;
- }
-
-
- public SimpleDialogBuilder setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- public SimpleDialogBuilder setMessage(int messageResourceId) {
- mMessage = mContext.getText(messageResourceId);
- return this;
- }
-
- /**
- * Allow to set resource string with HTML formatting and bind %s,%i.
- * This is workaround for https://code.google.com/p/android/issues/detail?id=2923
- */
- public SimpleDialogBuilder setMessage(int resourceId, Object... formatArgs){
- mMessage = Html.fromHtml(String.format(Html.toHtml(new SpannedString(mContext.getText(resourceId))), formatArgs));
- return this;
- }
-
- public SimpleDialogBuilder setMessage(CharSequence message) {
- mMessage = message;
- return this;
- }
-
- public SimpleDialogBuilder setPositiveButtonText(int textResourceId) {
- mPositiveButtonText = mContext.getString(textResourceId);
- return this;
- }
-
- public SimpleDialogBuilder setPositiveButtonText(String text) {
- mPositiveButtonText = text;
- return this;
- }
-
- public SimpleDialogBuilder setNegativeButtonText(int textResourceId) {
- mNegativeButtonText = mContext.getString(textResourceId);
- return this;
- }
-
- public SimpleDialogBuilder setNegativeButtonText(String text) {
- mNegativeButtonText = text;
- return this;
- }
-
- public SimpleDialogBuilder setNeutralButtonText(int textResourceId) {
- mNeutralButtonText = mContext.getString(textResourceId);
- return this;
- }
-
- public SimpleDialogBuilder setNeutralButtonText(String text) {
- mNeutralButtonText = text;
- return this;
- }
-
- /**
- * When there is neither positive nor negative button, default "close" button is created if it was enabled.
- * Default is true.
- */
- public SimpleDialogBuilder hideDefaultButton(boolean hide) {
- mShowDefaultButton = !hide;
- return this;
- }
-
- @Override
- protected Bundle prepareArguments() {
- if (mShowDefaultButton && mPositiveButtonText == null && mNegativeButtonText == null) {
- mPositiveButtonText = mContext.getString(R.string.dialog_close);
- }
-
- Bundle args = new Bundle();
- args.putCharSequence(SimpleDialogFragment.ARG_MESSAGE, mMessage);
- args.putString(SimpleDialogFragment.ARG_TITLE, mTitle);
- args.putString(SimpleDialogFragment.ARG_POSITIVE_BUTTON, mPositiveButtonText);
- args.putString(SimpleDialogFragment.ARG_NEGATIVE_BUTTON, mNegativeButtonText);
- args.putString(SimpleDialogFragment.ARG_NEUTRAL_BUTTON, mNeutralButtonText);
-
- return args;
- }
- }
+ public static SimpleDialogBuilder createBuilder(Context context, FragmentManager fragmentManager) {
+ return new SimpleDialogBuilder(context, fragmentManager, SimpleDialogFragment.class);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null) {
+ mRequestCode = getTargetRequestCode();
+ } else {
+ Bundle args = getArguments();
+ if (args != null) {
+ mRequestCode = args.getInt(BaseDialogFragment.ARG_REQUEST_CODE, 0);
+ }
+ }
+ }
+
+ /**
+ * Children can extend this to add more things to base builder.
+ */
+ @Override
+ protected BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
+ final String title = getTitle();
+ if (!TextUtils.isEmpty(title)) {
+ builder.setTitle(title);
+ }
+
+ final CharSequence message = getMessage();
+ if (!TextUtils.isEmpty(message)) {
+ builder.setMessage(message);
+ }
+
+ final String positiveButtonText = getPositiveButtonText();
+ if (!TextUtils.isEmpty(positiveButtonText)) {
+ builder.setPositiveButton(positiveButtonText, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ISimpleDialogListener listener = getDialogListener();
+ if (listener != null) {
+ listener.onPositiveButtonClicked(mRequestCode);
+ }
+ dismiss();
+ }
+ });
+ }
+
+ final String negativeButtonText = getNegativeButtonText();
+ if (!TextUtils.isEmpty(negativeButtonText)) {
+ builder.setNegativeButton(negativeButtonText, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ISimpleDialogListener listener = getDialogListener();
+ if (listener != null) {
+ listener.onNegativeButtonClicked(mRequestCode);
+ }
+ dismiss();
+ }
+ });
+ }
+
+ final String neutralButtonText = getNeutralButtonText();
+ if (!TextUtils.isEmpty(neutralButtonText)) {
+ builder.setNeutralButton(neutralButtonText, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ISimpleDialogListener listener = getDialogListener();
+ if (listener != null) {
+ listener.onNeutralButtonClicked(mRequestCode);
+ }
+ dismiss();
+ }
+ });
+ }
+
+ return builder;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ ISimpleDialogCancelListener listener = getCancelListener();
+ if (listener != null) {
+ listener.onCancelled(mRequestCode);
+ }
+ }
+
+ protected ISimpleDialogListener getDialogListener() {
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null) {
+ if (targetFragment instanceof ISimpleDialogListener) {
+ return (ISimpleDialogListener)targetFragment;
+ }
+ } else {
+ if (getActivity() instanceof ISimpleDialogListener) {
+ return (ISimpleDialogListener)getActivity();
+ }
+ }
+ return null;
+ }
+
+ protected ISimpleDialogCancelListener getCancelListener() {
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment != null) {
+ if (targetFragment instanceof ISimpleDialogCancelListener) {
+ return (ISimpleDialogCancelListener)targetFragment;
+ }
+ } else {
+ if (getActivity() instanceof ISimpleDialogCancelListener) {
+ return (ISimpleDialogCancelListener)getActivity();
+ }
+ }
+ return null;
+ }
+
+ public static class SimpleDialogBuilder extends BaseDialogBuilder {
+
+ protected SimpleDialogBuilder(Context context, FragmentManager fragmentManager,
+ Class extends SimpleDialogFragment> clazz) {
+ super(context, fragmentManager, clazz);
+ }
+
+ @Override
+ protected SimpleDialogBuilder self() {
+ return this;
+ }
+
+ @Override
+ protected Bundle prepareArguments() {
+ if (mDialogParams.showDefaultButton && mDialogParams.positiveButtonText == null &&
+ mDialogParams.negativeButtonText == null) {
+ mDialogParams.positiveButtonText = mDialogParams.context.getString(R.string.dialog_close);
+ }
+
+ Bundle args = new Bundle();
+ args.putCharSequence(BaseDialogFragment.ARG_MESSAGE, mDialogParams.message);
+ if (!TextUtils.isEmpty(mDialogParams.title)) {
+ args.putString(BaseDialogFragment.ARG_TITLE, mDialogParams.title.toString());
+ }
+ if (mDialogParams.positiveButtonText != null) {
+ args.putString(BaseDialogFragment.ARG_POSITIVE_BUTTON, mDialogParams.positiveButtonText
+ .toString());
+ }
+ if (mDialogParams.negativeButtonText != null) {
+ args.putString(BaseDialogFragment.ARG_NEGATIVE_BUTTON,
+ mDialogParams.negativeButtonText.toString());
+ }
+ if (mDialogParams.neutralButtonText != null) {
+ args.putString(BaseDialogFragment.ARG_NEUTRAL_BUTTON,
+ mDialogParams.neutralButtonText.toString());
+ }
+ args.putBoolean(BaseDialogFragment.ARG_CANCELABLE_ON_TOUCH_OUTSIDE,
+ mDialogParams.cancelableOnTouchOutside);
+ return args;
+ }
+ }
}
diff --git a/library/src/main/java/eu/inmite/android/lib/dialogs/TimePickerDialogFragment.java b/library/src/main/java/eu/inmite/android/lib/dialogs/TimePickerDialogFragment.java
index 0eeecfe..3e2e2d9 100644
--- a/library/src/main/java/eu/inmite/android/lib/dialogs/TimePickerDialogFragment.java
+++ b/library/src/main/java/eu/inmite/android/lib/dialogs/TimePickerDialogFragment.java
@@ -1,14 +1,14 @@
package eu.inmite.android.lib.dialogs;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
import android.content.Context;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.widget.TimePicker;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.TimeZone;
-
/**
* Dialog with a time picker. Implement {@link eu.inmite.android.lib.dialogs.IDateDialogListener} or {@link eu.inmite.android.lib.dialogs.IDateDialogCancelListener} to handle events.
*/
@@ -25,7 +25,7 @@ public static SimpleDialogBuilder createBuilder(Context context, FragmentManager
@Override
protected BaseDialogFragment.Builder build(BaseDialogFragment.Builder builder) {
builder = super.build(builder);
- mTimePicker = (TimePicker) LayoutInflater.from(getActivity()).inflate(R.layout.dialog_part_timepicker, null);
+ mTimePicker = (TimePicker)LayoutInflater.from(getActivity()).inflate(R.layout.dialog_part_timepicker, null);
mTimePicker.setIs24HourView(getArguments().getBoolean(ARG_24H));
builder.setView(mTimePicker);
diff --git a/library/src/main/res/color/sdl_primary_text_holo_dark.xml b/library/src/main/res/color/sdl_primary_text_holo_dark.xml
index ee118df..ce948f1 100644
--- a/library/src/main/res/color/sdl_primary_text_holo_dark.xml
+++ b/library/src/main/res/color/sdl_primary_text_holo_dark.xml
@@ -15,11 +15,11 @@
-->
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/library/src/main/res/color/sdl_primary_text_holo_light.xml b/library/src/main/res/color/sdl_primary_text_holo_light.xml
index b9b4b8d..7eb46c2 100644
--- a/library/src/main/res/color/sdl_primary_text_holo_light.xml
+++ b/library/src/main/res/color/sdl_primary_text_holo_light.xml
@@ -15,11 +15,11 @@
-->
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/res/drawable-hdpi/sdl_list_focused_holo.9.png b/library/src/main/res/drawable-hdpi/sdl_list_focused_holo.9.png
new file mode 100644
index 0000000..5552708
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_list_focused_holo.9.png differ
diff --git a/library/src/main/res/drawable-hdpi/sdl_list_longpressed_holo.9.png b/library/src/main/res/drawable-hdpi/sdl_list_longpressed_holo.9.png
new file mode 100644
index 0000000..4ea7afa
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_list_longpressed_holo.9.png differ
diff --git a/library/src/main/res/drawable-hdpi/sdl_list_pressed_holo_dark.9.png b/library/src/main/res/drawable-hdpi/sdl_list_pressed_holo_dark.9.png
new file mode 100644
index 0000000..5654cd6
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_list_pressed_holo_dark.9.png differ
diff --git a/library/src/main/res/drawable-hdpi/sdl_list_pressed_holo_light.9.png b/library/src/main/res/drawable-hdpi/sdl_list_pressed_holo_light.9.png
new file mode 100644
index 0000000..5654cd6
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_list_pressed_holo_light.9.png differ
diff --git a/library/src/main/res/drawable-hdpi/sdl_list_selector_disabled_holo_dark.9.png b/library/src/main/res/drawable-hdpi/sdl_list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..f6fd30d
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_list_selector_disabled_holo_dark.9.png differ
diff --git a/library/src/main/res/drawable-hdpi/sdl_list_selector_disabled_holo_light.9.png b/library/src/main/res/drawable-hdpi/sdl_list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000..ca8e9a2
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_list_selector_disabled_holo_light.9.png differ
diff --git a/library/src/main/res/drawable-hdpi/sdl_np_numberpicker_selection_divider.9.png b/library/src/main/res/drawable-hdpi/sdl_np_numberpicker_selection_divider.9.png
new file mode 100644
index 0000000..c9c72ba
Binary files /dev/null and b/library/src/main/res/drawable-hdpi/sdl_np_numberpicker_selection_divider.9.png differ
diff --git a/library/src/main/res/drawable-mdpi/sdl_list_focused_holo.9.png b/library/src/main/res/drawable-mdpi/sdl_list_focused_holo.9.png
new file mode 100644
index 0000000..00f05d8
Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sdl_list_focused_holo.9.png differ
diff --git a/library/src/main/res/drawable-mdpi/sdl_list_longpressed_holo.9.png b/library/src/main/res/drawable-mdpi/sdl_list_longpressed_holo.9.png
new file mode 100644
index 0000000..3bf8e03
Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sdl_list_longpressed_holo.9.png differ
diff --git a/library/src/main/res/drawable-mdpi/sdl_list_pressed_holo_dark.9.png b/library/src/main/res/drawable-mdpi/sdl_list_pressed_holo_dark.9.png
new file mode 100644
index 0000000..6e77525
Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sdl_list_pressed_holo_dark.9.png differ
diff --git a/library/src/main/res/drawable-mdpi/sdl_list_pressed_holo_light.9.png b/library/src/main/res/drawable-mdpi/sdl_list_pressed_holo_light.9.png
new file mode 100644
index 0000000..6e77525
Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sdl_list_pressed_holo_light.9.png differ
diff --git a/library/src/main/res/drawable-mdpi/sdl_list_selector_disabled_holo_dark.9.png b/library/src/main/res/drawable-mdpi/sdl_list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..92da2f0
Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sdl_list_selector_disabled_holo_dark.9.png differ
diff --git a/library/src/main/res/drawable-mdpi/sdl_np_numberpicker_selection_divider.9.png b/library/src/main/res/drawable-mdpi/sdl_np_numberpicker_selection_divider.9.png
new file mode 100644
index 0000000..076fc16
Binary files /dev/null and b/library/src/main/res/drawable-mdpi/sdl_np_numberpicker_selection_divider.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_list_focused_holo.9.png b/library/src/main/res/drawable-xhdpi/sdl_list_focused_holo.9.png
new file mode 100644
index 0000000..b545f8e
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_list_focused_holo.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_list_longpressed_holo.9.png b/library/src/main/res/drawable-xhdpi/sdl_list_longpressed_holo.9.png
new file mode 100644
index 0000000..eda10e6
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_list_longpressed_holo.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_list_pressed_holo_dark.9.png b/library/src/main/res/drawable-xhdpi/sdl_list_pressed_holo_dark.9.png
new file mode 100644
index 0000000..e4b3393
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_list_pressed_holo_dark.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_list_pressed_holo_light.9.png b/library/src/main/res/drawable-xhdpi/sdl_list_pressed_holo_light.9.png
new file mode 100644
index 0000000..e4b3393
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_list_pressed_holo_light.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_list_selector_disabled_holo_dark.9.png b/library/src/main/res/drawable-xhdpi/sdl_list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..88726b6
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_list_selector_disabled_holo_dark.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_list_selector_disabled_holo_light.9.png b/library/src/main/res/drawable-xhdpi/sdl_list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000..c6a7d4d
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_list_selector_disabled_holo_light.9.png differ
diff --git a/library/src/main/res/drawable-xhdpi/sdl_np_numberpicker_selection_divider.9.png b/library/src/main/res/drawable-xhdpi/sdl_np_numberpicker_selection_divider.9.png
new file mode 100644
index 0000000..97eb5fe
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sdl_np_numberpicker_selection_divider.9.png differ
diff --git a/library/src/main/res/drawable/sdl_item_background_holo_dark.xml b/library/src/main/res/drawable/sdl_item_background_holo_dark.xml
new file mode 100644
index 0000000..af33b52
--- /dev/null
+++ b/library/src/main/res/drawable/sdl_item_background_holo_dark.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/src/main/res/drawable/sdl_item_background_holo_light.xml b/library/src/main/res/drawable/sdl_item_background_holo_light.xml
new file mode 100644
index 0000000..c964fe4
--- /dev/null
+++ b/library/src/main/res/drawable/sdl_item_background_holo_light.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/src/main/res/drawable/sdl_list_selector_background_transition_holo_dark.xml b/library/src/main/res/drawable/sdl_list_selector_background_transition_holo_dark.xml
new file mode 100644
index 0000000..25a8793
--- /dev/null
+++ b/library/src/main/res/drawable/sdl_list_selector_background_transition_holo_dark.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/library/src/main/res/drawable/sdl_list_selector_background_transition_holo_light.xml b/library/src/main/res/drawable/sdl_list_selector_background_transition_holo_light.xml
new file mode 100644
index 0000000..7002509
--- /dev/null
+++ b/library/src/main/res/drawable/sdl_list_selector_background_transition_holo_light.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/library/src/main/res/layout/dialog_part_button.xml b/library/src/main/res/layout/dialog_part_button.xml
index b3d1019..66b3325 100644
--- a/library/src/main/res/layout/dialog_part_button.xml
+++ b/library/src/main/res/layout/dialog_part_button.xml
@@ -1,5 +1,5 @@
+ style="@style/SDL.Button" />
diff --git a/library/src/main/res/layout/dialog_part_button_panel.xml b/library/src/main/res/layout/dialog_part_button_panel.xml
index 0c9fc28..be48ce2 100644
--- a/library/src/main/res/layout/dialog_part_button_panel.xml
+++ b/library/src/main/res/layout/dialog_part_button_panel.xml
@@ -1,14 +1,14 @@
+ android:id="@+id/sdl__button_panel"
+ style="@style/SDL.Group.ButtonPanel">
-
+
-
+
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_button_separator.xml b/library/src/main/res/layout/dialog_part_button_separator.xml
index 748a65a..cddcff8 100644
--- a/library/src/main/res/layout/dialog_part_button_separator.xml
+++ b/library/src/main/res/layout/dialog_part_button_separator.xml
@@ -1,6 +1,6 @@
+ style="@style/SDL.ButtonSeparator"
+ android:id="@+id/dialog_button_separator" />
diff --git a/library/src/main/res/layout/dialog_part_custom.xml b/library/src/main/res/layout/dialog_part_custom.xml
index a3eeab2..dba08c7 100644
--- a/library/src/main/res/layout/dialog_part_custom.xml
+++ b/library/src/main/res/layout/dialog_part_custom.xml
@@ -1,8 +1,10 @@
-
+ android:id="@+id/sdl__customPanel"
+ style="@style/SDL.Group.Content">
+
+
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_datepicker.xml b/library/src/main/res/layout/dialog_part_datepicker.xml
index 8db41be..b18940e 100644
--- a/library/src/main/res/layout/dialog_part_datepicker.xml
+++ b/library/src/main/res/layout/dialog_part_datepicker.xml
@@ -1,5 +1,5 @@
\ No newline at end of file
+ android:id="@+id/sdl__datepicker"
+ style="@style/SDL.DatePicker" />
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_list.xml b/library/src/main/res/layout/dialog_part_list.xml
index d197970..fb34752 100644
--- a/library/src/main/res/layout/dialog_part_list.xml
+++ b/library/src/main/res/layout/dialog_part_list.xml
@@ -1,5 +1,5 @@
\ No newline at end of file
+ android:id="@+id/sdl__listview"
+ style="@style/SDL.ListView" />
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_message.xml b/library/src/main/res/layout/dialog_part_message.xml
index c07f512..c551862 100644
--- a/library/src/main/res/layout/dialog_part_message.xml
+++ b/library/src/main/res/layout/dialog_part_message.xml
@@ -1,8 +1,10 @@
-
+ android:id="@+id/sdl__contentPanel"
+ style="@style/SDL.Group.Wrap">
+
+
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_numberpicker.xml b/library/src/main/res/layout/dialog_part_numberpicker.xml
new file mode 100644
index 0000000..809a5e1
--- /dev/null
+++ b/library/src/main/res/layout/dialog_part_numberpicker.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/library/src/main/res/layout/dialog_part_progress.xml b/library/src/main/res/layout/dialog_part_progress.xml
index f849f95..b34b808 100644
--- a/library/src/main/res/layout/dialog_part_progress.xml
+++ b/library/src/main/res/layout/dialog_part_progress.xml
@@ -1,15 +1,16 @@
+ android:id="@+id/sdl__progressPanel"
+ style="@style/SDL.Group"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
-
+
-
+
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_timepicker.xml b/library/src/main/res/layout/dialog_part_timepicker.xml
index 9343790..f23112f 100644
--- a/library/src/main/res/layout/dialog_part_timepicker.xml
+++ b/library/src/main/res/layout/dialog_part_timepicker.xml
@@ -1,5 +1,5 @@
\ No newline at end of file
+ android:id="@+id/sdl__datepicker"
+ style="@style/SDL.DatePicker" />
\ No newline at end of file
diff --git a/library/src/main/res/layout/dialog_part_title.xml b/library/src/main/res/layout/dialog_part_title.xml
index 0b68822..d991918 100644
--- a/library/src/main/res/layout/dialog_part_title.xml
+++ b/library/src/main/res/layout/dialog_part_title.xml
@@ -1,15 +1,16 @@
-
+ style="@style/SDL.Group">
-
+
-
+
+
+
diff --git a/library/src/main/res/layout/number_picker_with_selector_wheel.xml b/library/src/main/res/layout/number_picker_with_selector_wheel.xml
new file mode 100644
index 0000000..5913418
--- /dev/null
+++ b/library/src/main/res/layout/number_picker_with_selector_wheel.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
diff --git a/library/src/main/res/values-v11/sdl__theme.xml b/library/src/main/res/values-v11/sdl__theme.xml
index 72a4dc7..5c7a18a 100644
--- a/library/src/main/res/values-v11/sdl__theme.xml
+++ b/library/src/main/res/values-v11/sdl__theme.xml
@@ -1,12 +1,12 @@
-
+
\ No newline at end of file
diff --git a/library/src/main/res/values/sdl__attrs.xml b/library/src/main/res/values/sdl__attrs.xml
index 8d6b2ae..10d3336 100644
--- a/library/src/main/res/values/sdl__attrs.xml
+++ b/library/src/main/res/values/sdl__attrs.xml
@@ -1,26 +1,52 @@
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/res/values/sdl__colors.xml b/library/src/main/res/values/sdl__colors.xml
index 3db8b38..a0bd188 100755
--- a/library/src/main/res/values/sdl__colors.xml
+++ b/library/src/main/res/values/sdl__colors.xml
@@ -1,25 +1,25 @@
-
- #FFFFFF
- #ff0099cc
- #ff0099cc
- @color/sdl_primary_text_holo_dark
- @android:color/transparent
- #990099cc
- #4C0099cc
- #12ffffff
- #eeffffff
-
- #333333
- #33B5E5
- #33B5E5
- @color/sdl_primary_text_holo_light
- @android:color/transparent
- #9933B5E5
- #4C33B5E5
- #25000000
- #ee000000
+
+ #FFFFFF
+ #ff0099cc
+ #ff0099cc
+ @color/sdl_primary_text_holo_dark
+ @android:color/transparent
+ #990099cc
+ #4C0099cc
+ #12ffffff
+ #eeffffff
+
+ #333333
+ #33B5E5
+ #33B5E5
+ @color/sdl_primary_text_holo_light
+ @android:color/transparent
+ #9933B5E5
+ #4C33B5E5
+ #25000000
+ #ee000000#fff3f3f3
@@ -27,4 +27,5 @@
#ff4c4c4c#ffb2b2b2
+ #00000000
diff --git a/library/src/main/res/values/sdl__dimens.xml b/library/src/main/res/values/sdl__dimens.xml
index bdfa573..a83064a 100644
--- a/library/src/main/res/values/sdl__dimens.xml
+++ b/library/src/main/res/values/sdl__dimens.xml
@@ -1,34 +1,34 @@
-
- 4dp
- 8dp
- 12dp
- 16dp
- 20dp
- 24dp
- 28dp
- 32dp
- 36dp
- 40dp
- 44dp
- 48dp
- 52dp
- 56dp
- 60dp
- 64dp
- 68dp
- 72dp
- 80dp
- 104dp
- 108dp
- 112dp
- 172dp
- 176dp
- 180dp
- 184dp
- 220dp
- 228dp
- 232dp
- 360dp
+
+ 4dp
+ 8dp
+ 12dp
+ 16dp
+ 20dp
+ 24dp
+ 28dp
+ 32dp
+ 36dp
+ 40dp
+ 44dp
+ 48dp
+ 52dp
+ 56dp
+ 60dp
+ 64dp
+ 68dp
+ 72dp
+ 80dp
+ 104dp
+ 108dp
+ 112dp
+ 172dp
+ 176dp
+ 180dp
+ 184dp
+ 220dp
+ 228dp
+ 232dp
+ 360dp
\ No newline at end of file
diff --git a/library/src/main/res/values/sdl__ids.xml b/library/src/main/res/values/sdl__ids.xml
index 047bc0f..28eb87c 100644
--- a/library/src/main/res/values/sdl__ids.xml
+++ b/library/src/main/res/values/sdl__ids.xml
@@ -3,4 +3,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/res/values/sdl__strings.xml b/library/src/main/res/values/sdl__strings.xml
index 18a8935..22ae5a5 100644
--- a/library/src/main/res/values/sdl__strings.xml
+++ b/library/src/main/res/values/sdl__strings.xml
@@ -1,4 +1,4 @@
- Close
+ Close
diff --git a/library/src/main/res/values/sdl__theme.xml b/library/src/main/res/values/sdl__theme.xml
index bb5400c..4ee3c01 100644
--- a/library/src/main/res/values/sdl__theme.xml
+++ b/library/src/main/res/values/sdl__theme.xml
@@ -1,163 +1,179 @@
-
-
-
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-