summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/DatePickerDialog.java14
-rw-r--r--core/java/android/app/Fragment.java14
-rw-r--r--core/java/android/app/FragmentManager.java172
-rw-r--r--core/java/android/content/ContentResolver.java4
-rw-r--r--core/java/android/hardware/UsbManager.java11
-rw-r--r--core/java/android/net/Proxy.java4
-rw-r--r--core/java/android/net/http/RequestHandle.java2
-rw-r--r--core/java/android/view/WindowManager.java5
-rw-r--r--core/java/android/webkit/WebView.java5
-rw-r--r--core/java/android/widget/AbsListView.java36
-rw-r--r--core/java/android/widget/CalendarView.java1398
-rw-r--r--core/java/android/widget/DatePicker.java468
-rw-r--r--core/java/android/widget/DayPicker.java1532
-rw-r--r--core/java/android/widget/NumberPicker.java396
-rw-r--r--core/java/android/widget/TimePicker.java77
-rw-r--r--core/java/com/android/internal/app/LocalePicker.java7
-rw-r--r--core/jni/Android.mk4
-rw-r--r--core/jni/AndroidRuntime.cpp3
-rw-r--r--core/jni/android_hardware_UsbManager.cpp61
-rw-r--r--core/res/res/layout/calendar_view.xml (renamed from core/res/res/layout/day_picker.xml)22
-rw-r--r--core/res/res/layout/date_picker.xml42
-rw-r--r--core/res/res/layout/date_picker_dialog.xml5
-rw-r--r--core/res/res/layout/time_picker.xml16
-rwxr-xr-xcore/res/res/values/attrs.xml67
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/public.xml29
-rw-r--r--core/res/res/values/styles.xml73
-rw-r--r--core/res/res/values/themes.xml22
28 files changed, 2451 insertions, 2041 deletions
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index e6e55ee..8b70370 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -118,18 +118,12 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener,
}
/**
- * Sets the range of years in which dates can be selected.
- * <p>
- * Note: If the range is set to a value that does not include the currently
- * selected date the value of the picker shown by this dialog will be
- * updated to the closest date in the range.
- * </p>
+ * Gets the {@link DatePicker} contained in this dialog.
*
- * @param startYear The start year of the range.
- * @param endYear The end year of the range.
+ * @return The calendar view.
*/
- public void setRange(int startYear, int endYear) {
- mDatePicker.setRange(startYear, endYear);
+ public DatePicker getDatePicker() {
+ return mDatePicker;
}
/**
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f06f2cf..a920814 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -323,6 +323,15 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
int mState = INITIALIZING;
+ // Non-null if the fragment's view hierarchy is currently animating away,
+ // meaning we need to wait a bit on completely destroying it. This is the
+ // animation that is running.
+ Animator mAnimatingAway;
+
+ // If mAnimatingAway != null, this is the state we should move to once the
+ // animation is done.
+ int mStateAfterAnimating;
+
// When instantiated from saved state, this is the saved state.
Bundle mSavedFragmentState;
SparseArray<Parcelable> mSavedViewState;
@@ -1240,6 +1249,11 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
if (mView != null) {
writer.print(prefix); writer.print("mView="); writer.println(mView);
}
+ if (mAnimatingAway != null) {
+ writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway);
+ writer.print(prefix); writer.print("mStateAfterAnimating=");
+ writer.println(mStateAfterAnimating);
+ }
if (mLoaderManager != null) {
writer.print(prefix); writer.println("Loader Manager:");
mLoaderManager.dump(prefix + " ", fd, writer, args);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index fe2ebed..3c98d67 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -38,6 +38,7 @@ import android.view.ViewGroup;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Interface for interacting with {@link Fragment} objects inside of an
@@ -331,6 +332,7 @@ final class FragmentManagerImpl extends FragmentManager {
boolean mNeedMenuInvalidate;
boolean mStateSaved;
+ boolean mDestroyed;
String mNoTransactionsBecause;
// Temporary vars for state save and restore.
@@ -473,23 +475,23 @@ final class FragmentManagerImpl extends FragmentManager {
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- if (mActive == null || mActive.size() <= 0) {
- return;
- }
-
- writer.print(prefix); writer.print("Active Fragments in ");
- writer.print(Integer.toHexString(System.identityHashCode(this)));
- writer.println(":");
-
String innerPrefix = prefix + " ";
- int N = mActive.size();
- for (int i=0; i<N; i++) {
- Fragment f = mActive.get(i);
- if (f != null) {
- writer.print(prefix); writer.print(" #"); writer.print(i);
- writer.print(": "); writer.println(f.toString());
- f.dump(innerPrefix, fd, writer, args);
+ int N;
+ if (mActive != null) {
+ N = mActive.size();
+ if (N > 0) {
+ writer.print(prefix); writer.print("Active Fragments in ");
+ writer.print(Integer.toHexString(System.identityHashCode(this)));
+ writer.println(":");
+ for (int i=0; i<N; i++) {
+ Fragment f = mActive.get(i);
+ writer.print(prefix); writer.print(" #"); writer.print(i);
+ writer.print(": "); writer.println(f);
+ if (f != null) {
+ f.dump(innerPrefix, fd, writer, args);
+ }
+ }
}
}
@@ -505,6 +507,18 @@ final class FragmentManagerImpl extends FragmentManager {
}
}
+ if (mCreatedMenus != null) {
+ N = mCreatedMenus.size();
+ if (N > 0) {
+ writer.print(prefix); writer.println("Fragments Created Menus:");
+ for (int i=0; i<N; i++) {
+ Fragment f = mCreatedMenus.get(i);
+ writer.print(prefix); writer.print(" #"); writer.print(i);
+ writer.print(": "); writer.println(f.toString());
+ }
+ }
+ }
+
if (mBackStack != null) {
N = mBackStack.size();
if (N > 0) {
@@ -517,6 +531,54 @@ final class FragmentManagerImpl extends FragmentManager {
}
}
}
+
+ synchronized (this) {
+ if (mBackStackIndices != null) {
+ N = mBackStackIndices.size();
+ if (N > 0) {
+ writer.print(prefix); writer.println("Back Stack Indices:");
+ for (int i=0; i<N; i++) {
+ BackStackRecord bs = mBackStackIndices.get(i);
+ writer.print(prefix); writer.print(" #"); writer.print(i);
+ writer.print(": "); writer.println(bs);
+ }
+ }
+ }
+
+ if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) {
+ writer.print(prefix); writer.print("mAvailBackStackIndices: ");
+ writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
+ }
+ }
+
+ if (mPendingActions != null) {
+ N = mPendingActions.size();
+ if (N > 0) {
+ writer.print(prefix); writer.println("Pending Actions:");
+ for (int i=0; i<N; i++) {
+ Runnable r = mPendingActions.get(i);
+ writer.print(prefix); writer.print(" #"); writer.print(i);
+ writer.print(": "); writer.println(r);
+ }
+ }
+ }
+
+ writer.print(prefix); writer.println("FragmentManager misc state:");
+ writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState);
+ writer.print(" mStateSaved="); writer.print(mStateSaved);
+ writer.print(" mDestroyed="); writer.println(mDestroyed);
+ if (mNeedMenuInvalidate) {
+ writer.print(prefix); writer.print(" mNeedMenuInvalidate=");
+ writer.println(mNeedMenuInvalidate);
+ }
+ if (mNoTransactionsBecause != null) {
+ writer.print(prefix); writer.print(" mNoTransactionsBecause=");
+ writer.println(mNoTransactionsBecause);
+ }
+ if (mAvailIndices != null && mAvailIndices.size() > 0) {
+ writer.print(prefix); writer.print(" mAvailIndices: ");
+ writer.println(Arrays.toString(mAvailIndices.toArray()));
+ }
}
Animator loadAnimator(Fragment fragment, int transit, boolean enter,
@@ -569,6 +631,14 @@ final class FragmentManagerImpl extends FragmentManager {
}
if (f.mState < newState) {
+ if (f.mAnimatingAway != null) {
+ // The fragment is currently being animated... but! Now we
+ // want to move our state back up. Give up on waiting for the
+ // animation, move to whatever the final state should be once
+ // the animation is done, and then we can proceed from there.
+ f.mAnimatingAway = null;
+ moveToState(f, f.mStateAfterAnimating, 0, 0);
+ }
switch (f.mState) {
case Fragment.INITIALIZING:
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
@@ -716,18 +786,26 @@ final class FragmentManagerImpl extends FragmentManager {
}
if (f.mView != null && f.mContainer != null) {
Animator anim = null;
- if (mCurState > Fragment.INITIALIZING) {
+ if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
anim = loadAnimator(f, transit, false,
transitionStyle);
}
if (anim != null) {
final ViewGroup container = f.mContainer;
final View view = f.mView;
+ final Fragment fragment = f;
container.startViewTransition(view);
+ f.mAnimatingAway = anim;
+ f.mStateAfterAnimating = newState;
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator anim) {
container.endViewTransition(view);
+ if (fragment.mAnimatingAway != null) {
+ fragment.mAnimatingAway = null;
+ moveToState(fragment, fragment.mStateAfterAnimating,
+ 0, 0);
+ }
}
});
anim.setTarget(f.mView);
@@ -741,25 +819,46 @@ final class FragmentManagerImpl extends FragmentManager {
}
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
- if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
- if (!f.mRetaining) {
+ if (mDestroyed) {
+ if (f.mAnimatingAway != null) {
+ // The fragment's containing activity is
+ // being destroyed, but this fragment is
+ // currently animating away. Stop the
+ // animation right now -- it is not needed,
+ // and we can't wait any more on destroying
+ // the fragment.
+ Animator anim = f.mAnimatingAway;
+ f.mAnimatingAway = null;
+ anim.cancel();
+ }
+ }
+ if (f.mAnimatingAway != null) {
+ // We are waiting for the fragment's view to finish
+ // animating away. Just make a note of the state
+ // the fragment now should move to once the animation
+ // is done.
+ f.mStateAfterAnimating = newState;
+ } else {
+ if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
+ if (!f.mRetaining) {
+ f.mCalled = false;
+ f.onDestroy();
+ if (!f.mCalled) {
+ throw new SuperNotCalledException("Fragment " + f
+ + " did not call through to super.onDestroy()");
+ }
+ }
+
f.mCalled = false;
- f.onDestroy();
+ f.onDetach();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onDestroy()");
+ + " did not call through to super.onDetach()");
}
+ f.mImmediateActivity = null;
+ f.mActivity = null;
+ f.mFragmentManager = null;
}
-
- f.mCalled = false;
- f.onDetach();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onDetach()");
- }
- f.mImmediateActivity = null;
- f.mActivity = null;
- f.mFragmentManager = null;
}
}
}
@@ -873,9 +972,19 @@ final class FragmentManagerImpl extends FragmentManager {
transitionStyle);
if (anim != null) {
anim.setTarget(fragment.mView);
+ // Delay the actual hide operation until the animation finishes, otherwise
+ // the fragment will just immediately disappear
+ final Fragment finalFragment = fragment;
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finalFragment.mView.setVisibility(View.GONE);
+ }
+ });
anim.start();
+ } else {
+ fragment.mView.setVisibility(View.GONE);
}
- fragment.mView.setVisibility(View.GONE);
}
if (fragment.mAdded && fragment.mHasMenu) {
mNeedMenuInvalidate = true;
@@ -1442,6 +1551,7 @@ final class FragmentManagerImpl extends FragmentManager {
}
public void dispatchDestroy() {
+ mDestroyed = true;
moveToState(Fragment.INITIALIZING, false);
mActivity = null;
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index da1aac4..d034229 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -201,6 +201,7 @@ public abstract class ContentResolver {
} catch (RemoteException e) {
return null;
} catch (java.lang.Exception e) {
+ Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
return null;
} finally {
releaseProvider(provider);
@@ -216,6 +217,9 @@ public abstract class ContentResolver {
return type;
} catch (RemoteException e) {
return null;
+ } catch (java.lang.Exception e) {
+ Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
+ return null;
}
}
diff --git a/core/java/android/hardware/UsbManager.java b/core/java/android/hardware/UsbManager.java
index 1003bf9..6022b12 100644
--- a/core/java/android/hardware/UsbManager.java
+++ b/core/java/android/hardware/UsbManager.java
@@ -105,6 +105,14 @@ public class UsbManager {
*/
public static final String USB_FUNCTION_DISABLED = "disabled";
+ public static final int getDeviceId(String name) {
+ return native_get_device_id(name);
+ }
+
+ public static final String getDeviceName(int id) {
+ return native_get_device_name(id);
+ }
+
private static File getFunctionEnableFile(String function) {
return new File("/sys/class/usb_composite/" + function + "/enable");
}
@@ -130,4 +138,7 @@ public class UsbManager {
return false;
}
}
+
+ private static native int native_get_device_id(String name);
+ private static native String native_get_device_name(int id);
}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 0ad80dd..f750122 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -65,7 +65,9 @@ public final class Proxy {
* that either the default connection or its proxy has changed.
* The intent will have the following extra value:</p>
* <ul>
- * <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy
+ * <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy. Non-null,
+ * though if the proxy is undefined the host string
+ * will be empty.
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 103fd94..2c48a04 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -308,7 +308,7 @@ public class RequestHandle {
String A2 = mMethod + ":" + mUrl;
// because we do not preemptively send authorization headers, nc is always 1
- String nc = "000001";
+ String nc = "00000001";
String cnonce = computeCnonce();
String digest = computeDigest(A1, A2, nonce, QOP, nc, cnonce);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e8792ff..c435c43 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -381,6 +381,11 @@ public interface WindowManager extends ViewManager {
*/
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
+ /**
+ * Window type: (mouse) pointer
+ * @hide
+ */
+ public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
/**
* End of types of system windows.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5b24c50..0a165e8 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4485,6 +4485,11 @@ public class WebView extends AbsoluteLayout
+ ", " + event + ", unicode=" + event.getUnicodeChar());
}
+ // don't implement accelerator keys here; defer to host application
+ if (event.isCtrlPressed()) {
+ return false;
+ }
+
if (mNativeClass == 0) {
return false;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ab98763..6d3f227 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -512,6 +512,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private AbsListView.PerformClick mPerformClick;
/**
+ * Delayed action for touch mode.
+ */
+ private Runnable mTouchModeReset;
+
+ /**
* This view is in transcript mode -- it shows the bottom of the list when the data
* changes
*/
@@ -2322,6 +2327,27 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mFlingStrictSpan.finish();
mFlingStrictSpan = null;
}
+
+ if (mFlingRunnable != null) {
+ removeCallbacks(mFlingRunnable);
+ }
+
+ if (mPositionScroller != null) {
+ removeCallbacks(mPositionScroller);
+ }
+
+ if (mClearScrollingCache != null) {
+ removeCallbacks(mClearScrollingCache);
+ }
+
+ if (mPerformClick != null) {
+ removeCallbacks(mPerformClick);
+ }
+
+ if (mTouchModeReset != null) {
+ removeCallbacks(mTouchModeReset);
+ mTouchModeReset = null;
+ }
}
@Override
@@ -3020,7 +3046,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
((TransitionDrawable) d).resetTransition();
}
}
- postDelayed(new Runnable() {
+ if (mTouchModeReset != null) {
+ removeCallbacks(mTouchModeReset);
+ }
+ mTouchModeReset = new Runnable() {
+ @Override
public void run() {
mTouchMode = TOUCH_MODE_REST;
child.setPressed(false);
@@ -3029,7 +3059,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
post(performClick);
}
}
- }, ViewConfiguration.getPressedStateDuration());
+ };
+ postDelayed(mTouchModeReset,
+ ViewConfiguration.getPressedStateDuration());
} else {
mTouchMode = TOUCH_MODE_REST;
updateSelectorState();
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
new file mode 100644
index 0000000..7ef61a8
--- /dev/null
+++ b/core/java/android/widget/CalendarView.java
@@ -0,0 +1,1398 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.android.internal.R;
+
+import android.annotation.Widget;
+import android.app.Service;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView.OnScrollListener;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import libcore.icu.LocaleData;
+
+/**
+ * This class is a calendar widget for displaying and selecting dates. The range
+ * of dates supported by this calendar is configurable. A user can select a date
+ * by taping on it and can scroll and fling the calendar to a desired date.
+ *
+ * @attr ref android.R.styleable#CalendarView_showWeekNumber
+ * @attr ref android.R.styleable#CalendarView_firstDayOfWeek
+ * @attr ref android.R.styleable#CalendarView_minDate
+ * @attr ref android.R.styleable#CalendarView_maxDate
+ * @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
+ * @attr ref android.R.styleable#CalendarView_dateTextAppearance
+ */
+@Widget
+public class CalendarView extends FrameLayout {
+
+ /**
+ * Tag for logging.
+ */
+ private static final String LOG_TAG = CalendarView.class.getSimpleName();
+
+ /**
+ * Default value whether to show week number.
+ */
+ private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
+
+ /**
+ * The number of milliseconds in a day.e
+ */
+ private static final long MILLIS_IN_DAY = 86400000L;
+
+ /**
+ * The number of day in a week.
+ */
+ private static final int DAYS_PER_WEEK = 7;
+
+ /**
+ * The number of milliseconds in a week.
+ */
+ private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
+
+ /**
+ * Affects when the month selection will change while scrolling upe
+ */
+ private static final int SCROLL_HYST_WEEKS = 2;
+
+ /**
+ * How long the GoTo fling animation should last.
+ */
+ private static final int GOTO_SCROLL_DURATION = 1000;
+
+ /**
+ * The duration of the adjustment upon a user scroll in milliseconds.
+ */
+ private static final int ADJUSTMENT_SCROLL_DURATION = 500;
+
+ /**
+ * How long to wait after receiving an onScrollStateChanged notification
+ * before acting on it.
+ */
+ private static final int SCROLL_CHANGE_DELAY = 40;
+
+ /**
+ * String for formatting the month name in the title text view.
+ */
+ private static final String FORMAT_MONTH_NAME = "MMMM, yyyy";
+
+ /**
+ * String for parsing dates.
+ */
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+ /**
+ * The default minimal date.
+ */
+ private static final String DEFAULT_MIN_DATE = "01/01/1900";
+
+ /**
+ * The default maximal date.
+ */
+ private static final String DEFAULT_MAX_DATE = "01/01/2100";
+
+ private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
+
+ private static final int DEFAULT_DATE_TEXT_SIZE = 14;
+
+ private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
+
+ private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
+
+ private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
+
+ private static final int UNSCALED_BOTTOM_BUFFER = 20;
+
+ private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
+
+ private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
+
+ private final int mWeekSeperatorLineWidth;
+
+ private final int mDateTextSize;
+
+ private final Drawable mSelectedDateVerticalBar;
+
+ private final int mSelectedDateVerticalBarWidth;
+
+ private final int mSelectedWeekBackgroundColor;
+
+ private final int mFocusedMonthDateColor;
+
+ private final int mUnfocusedMonthDateColor;
+
+ private final int mWeekSeparatorLineColor;
+
+ private final int mWeekNumberColor;
+
+ /**
+ * The top offset of the weeks list.
+ */
+ private int mListScrollTopOffset = 2;
+
+ /**
+ * The visible height of a week view.
+ */
+ private int mWeekMinVisibleHeight = 12;
+
+ /**
+ * The visible height of a week view.
+ */
+ private int mBottomBuffer = 20;
+
+ /**
+ * The number of shown weeks.
+ */
+ private int mShownWeekCount;
+
+ /**
+ * Flag whether to show the week number.
+ */
+ private boolean mShowWeekNumber;
+
+ /**
+ * The number of day per week to be shown.
+ */
+ private int mDaysPerWeek = 7;
+
+ /**
+ * The friction of the week list while flinging.
+ */
+ private float mFriction = .05f;
+
+ /**
+ * Scale for adjusting velocity of the week list while flinging.
+ */
+ private float mVelocityScale = 0.333f;
+
+ /**
+ * The adapter for the weeks list.
+ */
+ private WeeksAdapter mAdapter;
+
+ /**
+ * The weeks list.
+ */
+ private ListView mListView;
+
+ /**
+ * The name of the month to display.
+ */
+ private TextView mMonthName;
+
+ /**
+ * The header with week day names.
+ */
+ private ViewGroup mDayNamesHeader;
+
+ /**
+ * Cached labels for the week names header.
+ */
+ private String[] mDayLabels;
+
+ /**
+ * Temporary instance to avoid multiple instantiations.
+ */
+ private Calendar mTempDate = Calendar.getInstance();
+
+ /**
+ * The first day of the week.
+ */
+ private int mFirstDayOfWeek;
+
+ /**
+ * The first day of the focused month.
+ */
+ private Calendar mFirstDayOfMonth = Calendar.getInstance();
+
+ /**
+ * Which month should be displayed/highlighted [0-11].
+ */
+ private int mCurrentMonthDisplayed;
+
+ /**
+ * Used for tracking during a scroll.
+ */
+ private long mPreviousScrollPosition;
+
+ /**
+ * Used for tracking which direction the view is scrolling.
+ */
+ private boolean mIsScrollingUp = false;
+
+ /**
+ * The previous scroll state of the weeks ListView.
+ */
+ private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * The current scroll state of the weeks ListView.
+ */
+ private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * Listener for changes in the selected day.
+ */
+ private OnDateChangeListener mOnDateChangeListener;
+
+ /**
+ * Command for adjusting the position after a scroll/fling.
+ */
+ private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
+
+ /**
+ * The start date of the range supported by this picker.
+ */
+ private Calendar mMinDate = Calendar.getInstance();
+
+ /**
+ * The end date of the range supported by this picker.
+ */
+ private Calendar mMaxDate = Calendar.getInstance();
+
+ /**
+ * Date format for parsing dates.
+ */
+ private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+ /**
+ * The callback used to indicate the user changes the date.
+ */
+ public interface OnDateChangeListener {
+
+ /**
+ * Called upon change of the selected day.
+ *
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ * @param month The month that was set [0-11].
+ * @param dayOfMonth The day of the month that was set.
+ */
+ public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth);
+ }
+
+ public CalendarView(Context context) {
+ this(context, null);
+ }
+
+ public CalendarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CalendarView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, 0);
+
+ TypedValue calendarViewStyle = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.calendarViewStyle, calendarViewStyle, true);
+ TypedArray attributesArray = context.obtainStyledAttributes(calendarViewStyle.resourceId,
+ R.styleable.CalendarView);
+ mShowWeekNumber = attributesArray.getBoolean(R.styleable.CalendarView_showWeekNumber,
+ DEFAULT_SHOW_WEEK_NUMBER);
+ mFirstDayOfWeek = attributesArray.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ String minDate = attributesArray.getString(R.styleable.CalendarView_minDate);
+ if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
+ parseDate(DEFAULT_MIN_DATE, mMinDate);
+ }
+ String maxDate = attributesArray.getString(R.styleable.CalendarView_maxDate);
+ if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
+ parseDate(DEFAULT_MAX_DATE, mMaxDate);
+ }
+ mShownWeekCount = attributesArray.getInt(R.styleable.CalendarView_shownWeekCount,
+ DEFAULT_SHOWN_WEEK_COUNT);
+ mSelectedWeekBackgroundColor = attributesArray.getColor(
+ R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
+ mFocusedMonthDateColor = attributesArray.getColor(
+ R.styleable.CalendarView_focusedMonthDateColor, 0);
+ mUnfocusedMonthDateColor = attributesArray.getColor(
+ R.styleable.CalendarView_unfocusedMonthDateColor, 0);
+ mWeekSeparatorLineColor = attributesArray.getColor(
+ R.styleable.CalendarView_weekSeparatorLineColor, 0);
+ mWeekNumberColor = attributesArray.getColor(R.styleable.CalendarView_weekNumberColor, 0);
+ mSelectedDateVerticalBar = attributesArray.getDrawable(
+ R.styleable.CalendarView_selectedDateVerticalBar);
+
+ int dateTextAppearanceResId= attributesArray.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
+ TypedArray dateTextAppearance = context.obtainStyledAttributes(dateTextAppearanceResId,
+ com.android.internal.R.styleable.TextAppearance);
+ mDateTextSize = dateTextAppearance.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
+
+ int weekDayTextAppearanceResId = attributesArray.getResourceId(
+ R.styleable.CalendarView_weekDayTextAppearance,
+ DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+ attributesArray.recycle();
+
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
+ mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
+ mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_BOTTOM_BUFFER, displayMetrics);
+ mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
+ mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
+
+ LayoutInflater layoutInflater = (LayoutInflater) mContext
+ .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
+ View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
+ addView(content);
+
+ mListView = (ListView) findViewById(R.id.list);
+ mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
+ mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
+
+ setUpHeader(weekDayTextAppearanceResId);
+ setUpListView();
+ setUpAdapter();
+
+ // go to today now
+ mTempDate.setTimeInMillis(System.currentTimeMillis());
+ goTo(mTempDate, false, true, true);
+ invalidate();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mListView.setEnabled(enabled);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mListView.isEnabled();
+ }
+
+ /**
+ * Gets the minimal date supported by this {@link CalendarView} in milliseconds
+ * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
+ * zone.
+ * <p>
+ * Note: The default minimal date is 01/01/1900.
+ * <p>
+ *
+ * @return The minimal supported date.
+ */
+ public long getMinDate() {
+ return mMinDate.getTimeInMillis();
+ }
+
+ /**
+ * Sets the minimal date supported by this {@link CalendarView} in milliseconds
+ * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
+ * zone.
+ *
+ * @param minDate The minimal supported date.
+ */
+ public void setMinDate(long minDate) {
+ mTempDate.setTimeInMillis(minDate);
+ if (isSameDate(mTempDate, mMinDate)) {
+ return;
+ }
+ mMinDate.setTimeInMillis(minDate);
+ // reinitialize the adapter since its range depends on min date
+ mAdapter.init();
+ Calendar date = mAdapter.mSelectedDate;
+ if (date.before(mMinDate)) {
+ setDate(mMinDate.getTimeInMillis());
+ } else {
+ // we go to the current date to force the ListView to query its
+ // adapter for the shown views since we have changed the adapter
+ // range and the base from which the later calculates item indices
+ // note that calling setDate will not work since the date is the same
+ goTo(date, false, true, false);
+ }
+ }
+
+ /**
+ * Gets the maximal date supported by this {@link CalendarView} in milliseconds
+ * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
+ * zone.
+ * <p>
+ * Note: The default maximal date is 01/01/2100.
+ * <p>
+ *
+ * @return The maximal supported date.
+ */
+ public long getMaxDate() {
+ return mMaxDate.getTimeInMillis();
+ }
+
+ /**
+ * Sets the maximal date supported by this {@link CalendarView} in milliseconds
+ * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
+ * zone.
+ *
+ * @param maxDate The maximal supported date.
+ */
+ public void setMaxDate(long maxDate) {
+ mTempDate.setTimeInMillis(maxDate);
+ if (isSameDate(mTempDate, mMaxDate)) {
+ return;
+ }
+ mMaxDate.setTimeInMillis(maxDate);
+ // reinitialize the adapter since its range depends on max date
+ mAdapter.init();
+ Calendar date = mAdapter.mSelectedDate;
+ if (date.after(mMaxDate)) {
+ setDate(mMaxDate.getTimeInMillis());
+ } else {
+ // we go to the current date to force the ListView to query its
+ // adapter for the shown views since we have changed the adapter
+ // range and the base from which the later calculates item indices
+ // note that calling setDate will not work since the date is the same
+ goTo(date, false, true, false);
+ }
+ }
+
+ /**
+ * Sets whether to show the week number.
+ *
+ * @param showWeekNumber True to show the week number.
+ */
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ if (mShowWeekNumber == showWeekNumber) {
+ return;
+ }
+ mShowWeekNumber = showWeekNumber;
+ mAdapter.notifyDataSetChanged();
+ setUpHeader(DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+ }
+
+ /**
+ * Gets whether to show the week number.
+ *
+ * @return True if showing the week number.
+ */
+ public boolean getShowWeekNumber() {
+ return mShowWeekNumber;
+ }
+
+ /**
+ * Gets the first day of week.
+ *
+ * @return The first day of the week conforming to the {@link CalendarView}
+ * APIs.
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ * @see Calendar#SUNDAY
+ */
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
+ /**
+ * Sets the first day of week.
+ *
+ * @param firstDayOfWeek The first day of the week conforming to the
+ * {@link CalendarView} APIs.
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ * @see Calendar#SUNDAY
+ */
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ if (mFirstDayOfWeek == firstDayOfWeek) {
+ return;
+ }
+ mFirstDayOfWeek = firstDayOfWeek;
+ mAdapter.init();
+ mAdapter.notifyDataSetChanged();
+ setUpHeader(DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+ }
+
+ /**
+ * Sets the listener to be notified upon selected date change.
+ *
+ * @param listener The listener to be notified.
+ */
+ public void setOnDateChangeListener(OnDateChangeListener listener) {
+ mOnDateChangeListener = listener;
+ }
+
+ /**
+ * Gets the selected date in milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ *
+ * @return The selected date.
+ */
+ public long getDate() {
+ return mAdapter.mSelectedDate.getTimeInMillis();
+ }
+
+ /**
+ * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ *
+ * @param date The selected date.
+ *
+ * @throws IllegalArgumentException of the provided date is before the
+ * minimal or after the maximal date.
+ *
+ * @see #setDate(long, boolean, boolean)
+ * @see #setMinDate(long)
+ * @see #setMaxDate(long)
+ */
+ public void setDate(long date) {
+ setDate(date, false, false);
+ }
+
+ /**
+ * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ *
+ * @param date The date.
+ * @param animate Whether to animate the scroll to the current date.
+ * @param center Whether to center the current date even if it is already visible.
+ *
+ * @throws IllegalArgumentException of the provided date is before the
+ * minimal or after the maximal date.
+ *
+ * @see #setMinDate(long)
+ * @see #setMaxDate(long)
+ */
+ public void setDate(long date, boolean animate, boolean center) {
+ mTempDate.setTimeInMillis(date);
+ if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
+ return;
+ }
+ goTo(mTempDate, animate, true, center);
+ }
+
+ /**
+ * @return True if the <code>firstDate</code> is the same as the <code>
+ * secondDate</code>.
+ */
+ private boolean isSameDate(Calendar firstDate, Calendar secondDate) {
+ return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
+ && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
+ }
+
+ /**
+ * Creates a new adapter if necessary and sets up its parameters.
+ */
+ private void setUpAdapter() {
+ if (mAdapter == null) {
+ mAdapter = new WeeksAdapter(getContext());
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ if (mOnDateChangeListener != null) {
+ Calendar selectedDay = mAdapter.getSelectedDay();
+ mOnDateChangeListener.onSelectedDayChange(CalendarView.this,
+ selectedDay.get(Calendar.YEAR),
+ selectedDay.get(Calendar.MONTH),
+ selectedDay.get(Calendar.DAY_OF_MONTH));
+ }
+ }
+ });
+ mListView.setAdapter(mAdapter);
+ }
+
+ // refresh the view with the new parameters
+ mAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Sets up the strings to be used by the header.
+ */
+ private void setUpHeader(int weekDayTextAppearanceResId) {
+ mDayLabels = new String[mDaysPerWeek];
+ for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
+ int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
+ mDayLabels[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+ DateUtils.LENGTH_SHORTEST);
+ }
+
+ TextView label = (TextView) mDayNamesHeader.getChildAt(0);
+ if (mShowWeekNumber) {
+ label.setVisibility(View.VISIBLE);
+ } else {
+ label.setVisibility(View.GONE);
+ }
+ for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
+ label = (TextView) mDayNamesHeader.getChildAt(i);
+ if (weekDayTextAppearanceResId > -1) {
+ label.setTextAppearance(mContext, weekDayTextAppearanceResId);
+ }
+ if (i < mDaysPerWeek + 1) {
+ label.setText(mDayLabels[i - 1]);
+ label.setVisibility(View.VISIBLE);
+ } else {
+ label.setVisibility(View.GONE);
+ }
+ }
+ mDayNamesHeader.invalidate();
+ }
+
+ /**
+ * Sets all the required fields for the list view.
+ */
+ private void setUpListView() {
+ // Configure the listview
+ mListView.setDivider(null);
+ mListView.setItemsCanFocus(true);
+ mListView.setVerticalScrollBarEnabled(false);
+ mListView.setOnScrollListener(new OnScrollListener() {
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ CalendarView.this.onScrollStateChanged(view, scrollState);
+ }
+
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ CalendarView.this.onScroll(view, firstVisibleItem, visibleItemCount,
+ totalItemCount);
+ }
+ });
+ // Make the scrolling behavior nicer
+ mListView.setFriction(mFriction);
+ mListView.setVelocityScale(mVelocityScale);
+ }
+
+ /**
+ * This moves to the specified time in the view. If the time is not already
+ * in range it will move the list so that the first of the month containing
+ * the time is at the top of the view. If the new time is already in view
+ * the list will not be scrolled unless forceScroll is true. This time may
+ * optionally be highlighted as selected as well.
+ *
+ * @param date The time to move to.
+ * @param animate Whether to scroll to the given time or just redraw at the
+ * new location.
+ * @param setSelected Whether to set the given time as selected.
+ * @param forceScroll Whether to recenter even if the time is already
+ * visible.
+ *
+ * @throws IllegalArgumentException of the provided date is before the
+ * range start of after the range end.
+ */
+ private void goTo(Calendar date, boolean animate, boolean setSelected, boolean forceScroll) {
+ if (date.before(mMinDate) || date.after(mMaxDate)) {
+ throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
+ + " and " + mMaxDate.getTime());
+ }
+ // Find the first and last entirely visible weeks
+ int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+ View firstChild = mListView.getChildAt(0);
+ if (firstChild != null && firstChild.getTop() < 0) {
+ firstFullyVisiblePosition++;
+ }
+ int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
+ if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
+ lastFullyVisiblePosition--;
+ }
+ if (setSelected) {
+ mAdapter.setSelectedDay(date);
+ }
+ // Get the week we're going to
+ int position = getWeeksSinceMinDate(date);
+
+ // Check if the selected day is now outside of our visible range
+ // and if so scroll to the month that contains it
+ if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
+ || forceScroll) {
+ mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
+ mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
+
+ setMonthDisplayed(mFirstDayOfMonth);
+ position = getWeeksSinceMinDate(mFirstDayOfMonth);
+
+ mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
+ if (animate) {
+ mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
+ GOTO_SCROLL_DURATION);
+ } else {
+ mListView.setSelectionFromTop(position, mListScrollTopOffset);
+ // Perform any after scroll operations that are needed
+ onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
+ }
+ } else if (setSelected) {
+ // Otherwise just set the selection
+ setMonthDisplayed(date);
+ }
+ }
+
+ /**
+ * Parses the given <code>date</code> and in case of success sets
+ * the result to the <code>outDate</code>.
+ *
+ * @return True if the date was parsed.
+ */
+ private boolean parseDate(String date, Calendar outDate) {
+ try {
+ outDate.setTime(mDateFormat.parse(date));
+ return true;
+ } catch (ParseException e) {
+ Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
+ return false;
+ }
+ }
+
+ /**
+ * Called when a <code>view</code> transitions to a new <code>scrollState
+ * </code>.
+ */
+ private void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
+ }
+
+ /**
+ * Updates the title and selected month if the <code>view</code> has moved to a new
+ * month.
+ */
+ private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ WeekView child = (WeekView) view.getChildAt(0);
+ if (child == null) {
+ return;
+ }
+
+ // Figure out where we are
+ long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
+
+ // If we have moved since our last call update the direction
+ if (currScroll < mPreviousScrollPosition) {
+ mIsScrollingUp = true;
+ } else if (currScroll > mPreviousScrollPosition) {
+ mIsScrollingUp = false;
+ } else {
+ return;
+ }
+
+ // Use some hysteresis for checking which month to highlight. This
+ // causes the month to transition when two full weeks of a month are
+ // visible when scrolling up, and when the first day in a month reaches
+ // the top of the screen when scrolling down.
+ int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
+ if (mIsScrollingUp) {
+ child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
+ } else if (offset != 0) {
+ child = (WeekView) view.getChildAt(offset);
+ }
+
+ // Find out which month we're moving into
+ int month;
+ if (mIsScrollingUp) {
+ month = child.getMonthOfFirstWeekDay();
+ } else {
+ month = child.getMonthOfLastWeekDay();
+ }
+
+ // And how it relates to our current highlighted month
+ int monthDiff;
+ if (mCurrentMonthDisplayed == 11 && month == 0) {
+ monthDiff = 1;
+ } else if (mCurrentMonthDisplayed == 0 && month == 11) {
+ monthDiff = -1;
+ } else {
+ monthDiff = month - mCurrentMonthDisplayed;
+ }
+
+ // Only switch months if we're scrolling away from the currently
+ // selected month
+ if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
+ Calendar firstDay = child.getFirstDay();
+ if (mIsScrollingUp) {
+ firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
+ } else {
+ firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
+ }
+ setMonthDisplayed(firstDay);
+ }
+ mPreviousScrollPosition = currScroll;
+ mPreviousScrollState = mCurrentScrollState;
+ }
+
+ /**
+ * Sets the month displayed at the top of this view based on time. Override
+ * to add custom events when the title is changed.
+ *
+ * @param calendar A day in the new focus month.
+ */
+ private void setMonthDisplayed(Calendar calendar) {
+ mMonthName.setText(DateFormat.format(FORMAT_MONTH_NAME, calendar));
+ mMonthName.invalidate();
+ mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
+ mAdapter.setFocusMonth(mCurrentMonthDisplayed);
+ // TODO Send Accessibility Event
+ }
+
+ /**
+ * @return Returns the number of weeks between the current <code>date</code>
+ * and the <code>mMinDate</code>.
+ */
+ private int getWeeksSinceMinDate(Calendar date) {
+ if (date.before(mMinDate)) {
+ throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
+ + " does not precede toDate: " + date.getTime());
+ }
+ int fromDateDayOfWeek = mMinDate.get(Calendar.DAY_OF_WEEK);
+ long diff = (fromDateDayOfWeek - mFirstDayOfWeek) * MILLIS_IN_DAY;
+ long refDay = mMinDate.getTimeInMillis() - diff;
+ return (int) ((date.getTimeInMillis() - refDay) / MILLIS_IN_WEEK);
+ }
+
+ /**
+ * Command responsible for acting upon scroll state changes.
+ */
+ private class ScrollStateRunnable implements Runnable {
+ private AbsListView mView;
+
+ private int mNewState;
+
+ /**
+ * Sets up the runnable with a short delay in case the scroll state
+ * immediately changes again.
+ *
+ * @param view The list view that changed state
+ * @param scrollState The new state it changed to
+ */
+ public void doScrollStateChange(AbsListView view, int scrollState) {
+ mView = view;
+ mNewState = scrollState;
+ removeCallbacks(this);
+ postDelayed(this, SCROLL_CHANGE_DELAY);
+ }
+
+ public void run() {
+ mCurrentScrollState = mNewState;
+ // Fix the position after a scroll or a fling ends
+ if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
+ && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
+ View child = mView.getChildAt(0);
+ if (child == null) {
+ // The view is no longer visible, just return
+ return;
+ }
+ int dist = child.getBottom() - mListScrollTopOffset;
+ if (dist > mListScrollTopOffset) {
+ if (mIsScrollingUp) {
+ mView.smoothScrollBy(dist - child.getHeight(), ADJUSTMENT_SCROLL_DURATION);
+ } else {
+ mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
+ }
+ }
+ }
+ mPreviousScrollState = mNewState;
+ }
+ }
+
+ /**
+ * <p>
+ * This is a specialized adapter for creating a list of weeks with
+ * selectable days. It can be configured to display the week number, start
+ * the week on a given day, show a reduced number of days, or display an
+ * arbitrary number of weeks at a time.
+ * </p>
+ */
+ private class WeeksAdapter extends BaseAdapter implements OnTouchListener {
+
+ private int mSelectedWeek;
+
+ private GestureDetector mGestureDetector;
+
+ private int mFocusedMonth;
+
+ private final Calendar mSelectedDate = Calendar.getInstance();
+
+ private int mTotalWeekCount;
+
+ public WeeksAdapter(Context context) {
+ mContext = context;
+ mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
+ init();
+ }
+
+ /**
+ * Set up the gesture detector and selected time
+ */
+ private void init() {
+ mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+ mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
+ if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
+ || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
+ mTotalWeekCount++;
+ }
+ }
+
+ /**
+ * Updates the selected day and related parameters.
+ *
+ * @param selectedDay The time to highlight
+ */
+ public void setSelectedDay(Calendar selectedDay) {
+ if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
+ && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
+ return;
+ }
+ mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
+ mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+ mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * @return The selected day of month.
+ */
+ public Calendar getSelectedDay() {
+ return mSelectedDate;
+ }
+
+ @Override
+ public int getCount() {
+ return mTotalWeekCount;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ WeekView weekView = null;
+ if (convertView != null) {
+ weekView = (WeekView) convertView;
+ } else {
+ weekView = new WeekView(mContext);
+ android.widget.AbsListView.LayoutParams params =
+ new android.widget.AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ weekView.setLayoutParams(params);
+ weekView.setClickable(true);
+ weekView.setOnTouchListener(this);
+ }
+
+ int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
+ Calendar.DAY_OF_WEEK) : -1;
+ weekView.init(position, selectedWeekDay, mFocusedMonth);
+
+ return weekView;
+ }
+
+ /**
+ * Changes which month is in focus and updates the view.
+ *
+ * @param month The month to show as in focus [0-11]
+ */
+ public void setFocusMonth(int month) {
+ if (mFocusedMonth == month) {
+ return;
+ }
+ mFocusedMonth = month;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
+ WeekView weekView = (WeekView) v;
+ weekView.getDayFromLocation(event.getX(), mTempDate);
+ // it is possible that the touched day is outside the valid range
+ // we draw whole weeks but range end can fall not on the week end
+ if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+ return true;
+ }
+ onDateTapped(mTempDate);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Maintains the same hour/min/sec but moves the day to the tapped day.
+ *
+ * @param day The day that was tapped
+ */
+ private void onDateTapped(Calendar day) {
+ setSelectedDay(day);
+ setMonthDisplayed(day);
+ }
+
+ /**
+ * This is here so we can identify single tap events and set the
+ * selected day correctly
+ */
+ class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * This is a dynamic view for drawing a single week. It can be configured to
+ * display the week number, start the week on a given day, or show a reduced
+ * number of days. It is intended for use as a single view within a
+ * ListView. See {@link WeeksAdapter} for usage.
+ * </p>
+ */
+ private class WeekView extends View {
+
+ private final Rect mTempRect = new Rect();
+
+ private final Paint mDrawPaint = new Paint();
+
+ private final Paint mMonthNumDrawPaint = new Paint();
+
+ // Cache the number strings so we don't have to recompute them each time
+ private String[] mDayNumbers;
+
+ // Quick lookup for checking which days are in the focus month
+ private boolean[] mFocusDay;
+
+ // The first day displayed by this item
+ private Calendar mFirstDay;
+
+ // The month of the first day in this week
+ private int mMonthOfFirstWeekDay = -1;
+
+ // The month of the last day in this week
+ private int mLastWeekDayMonth = -1;
+
+ // The position of this week, equivalent to weeks since the week of Jan
+ // 1st, 1900
+ private int mWeek = -1;
+
+ // Quick reference to the width of this view, matches parent
+ private int mWidth;
+
+ // The height this view should draw at in pixels, set by height param
+ private int mHeight;
+
+ // If this view contains the selected day
+ private boolean mHasSelectedDay = false;
+
+ // Which day is selected [0-6] or -1 if no day is selected
+ private int mSelectedDay = -1;
+
+ // The number of days + a spot for week number if it is displayed
+ private int mNumCells;
+
+ // The left edge of the selected day
+ private int mSelectedLeft = -1;
+
+ // The right edge of the selected day
+ private int mSelectedRight = -1;
+
+ public WeekView(Context context) {
+ super(context);
+
+ mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
+ .getPaddingBottom()) / mShownWeekCount;
+
+ // Sets up any standard paints that will be used
+ setPaintProperties();
+ }
+
+ /**
+ * Initializes this week view.
+ *
+ * @param weekNumber The number of the week this view represents. The
+ * week number is a zero based index of the weeks since
+ * {@link CalendarView#getMinDate()}.
+ * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
+ * selected day.
+ * @param focusedMonth The month that is currently in focus i.e.
+ * highlighted.
+ */
+ public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
+ mSelectedDay = selectedWeekDay;
+ mHasSelectedDay = mSelectedDay != -1;
+ mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
+ mWeek = weekNumber;
+ mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
+ mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
+ mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
+
+ // Allocate space for caching the day numbers and focus values
+ mDayNumbers = new String[mNumCells];
+ mFocusDay = new boolean[mNumCells];
+
+ // If we're showing the week number calculate it based on Monday
+ int i = 0;
+ if (mShowWeekNumber) {
+ mDayNumbers[0] = Integer.toString(mTempDate.get(Calendar.WEEK_OF_YEAR));
+ i++;
+ }
+
+ // Now adjust our starting day based on the start day of the week
+ int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
+ mTempDate.add(Calendar.DAY_OF_MONTH, diff);
+
+ mFirstDay = (Calendar) mTempDate.clone();
+ mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
+
+ for (; i < mNumCells; i++) {
+ mFocusDay[i] = (mTempDate.get(Calendar.MONTH) == focusedMonth);
+ // do not draw dates outside the valid range to avoid user confusion
+ if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+ mDayNumbers[i] = "";
+ } else {
+ mDayNumbers[i] = Integer.toString(mTempDate.get(Calendar.DAY_OF_MONTH));
+ }
+ mTempDate.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ // We do one extra add at the end of the loop, if that pushed us to
+ // new month undo it
+ if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
+ mTempDate.add(Calendar.DAY_OF_MONTH, -1);
+ }
+ mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
+
+ updateSelectionPositions();
+ }
+
+ /**
+ * Sets up the text and style properties for painting.
+ */
+ private void setPaintProperties() {
+ mDrawPaint.setFakeBoldText(false);
+ mDrawPaint.setAntiAlias(true);
+ mDrawPaint.setTextSize(mDateTextSize);
+ mDrawPaint.setStyle(Style.FILL);
+
+ mMonthNumDrawPaint.setFakeBoldText(true);
+ mMonthNumDrawPaint.setAntiAlias(true);
+ mMonthNumDrawPaint.setTextSize(mDateTextSize);
+ mMonthNumDrawPaint.setColor(mFocusedMonthDateColor);
+ mMonthNumDrawPaint.setStyle(Style.FILL);
+ mMonthNumDrawPaint.setTextAlign(Align.CENTER);
+ }
+
+ /**
+ * Returns the month of the first day in this week.
+ *
+ * @return The month the first day of this view is in.
+ */
+ public int getMonthOfFirstWeekDay() {
+ return mMonthOfFirstWeekDay;
+ }
+
+ /**
+ * Returns the month of the last day in this week
+ *
+ * @return The month the last day of this view is in
+ */
+ public int getMonthOfLastWeekDay() {
+ return mLastWeekDayMonth;
+ }
+
+ /**
+ * Returns the first day in this view.
+ *
+ * @return The first day in the view.
+ */
+ public Calendar getFirstDay() {
+ return mFirstDay;
+ }
+
+ /**
+ * Calculates the day that the given x position is in, accounting for
+ * week number. Returns a Time referencing that day or null if
+ *
+ * @param x The x position of the touch eventy
+ */
+ public void getDayFromLocation(float x, Calendar outCalendar) {
+ int dayStart = mShowWeekNumber ? mWidth / mNumCells : 0;
+ if (x < dayStart || x > mWidth) {
+ outCalendar.clear();
+ return;
+ }
+ // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
+ int dayPosition = (int) ((x - dayStart) * mDaysPerWeek
+ / (mWidth - dayStart));
+ outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
+ outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawBackground(canvas);
+ drawWeekNumbers(canvas);
+ drawWeekSeparators(canvas);
+ drawSelectedDateVerticalBars(canvas);
+ }
+
+ /**
+ * This draws the selection highlight if a day is selected in this week.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawBackground(Canvas canvas) {
+ if (!mHasSelectedDay) {
+ return;
+ }
+ mDrawPaint.setColor(mSelectedWeekBackgroundColor);
+
+ mTempRect.top = mWeekSeperatorLineWidth;
+ mTempRect.bottom = mHeight;
+ mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
+ mTempRect.right = mSelectedLeft - 2;
+ canvas.drawRect(mTempRect, mDrawPaint);
+
+ mTempRect.left = mSelectedRight + 3;
+ mTempRect.right = mWidth;
+ canvas.drawRect(mTempRect, mDrawPaint);
+ }
+
+ /**
+ * Draws the week and month day numbers for this week.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawWeekNumbers(Canvas canvas) {
+ float textHeight = mDrawPaint.getTextSize();
+ int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
+ int nDays = mNumCells;
+
+ mDrawPaint.setTextAlign(Align.CENTER);
+ int i = 0;
+ int divisor = 2 * nDays;
+ if (mShowWeekNumber) {
+ mDrawPaint.setColor(mWeekNumberColor);
+ int x = mWidth / divisor;
+ canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+ i++;
+ }
+ for (; i < nDays; i++) {
+ mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+ : mUnfocusedMonthDateColor);
+ int x = (2 * i + 1) * mWidth / divisor;
+ canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
+ }
+ }
+
+ /**
+ * Draws a horizontal line for separating the weeks.
+ *
+ * @param canvas The canvas to draw on.
+ */
+ private void drawWeekSeparators(Canvas canvas) {
+ // If it is the topmost fully visible child do not draw separator line
+ int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+ if (mListView.getChildAt(0).getTop() < 0) {
+ firstFullyVisiblePosition++;
+ }
+ if (firstFullyVisiblePosition == mWeek) {
+ return;
+ }
+ mDrawPaint.setColor(mWeekSeparatorLineColor);
+ mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
+ float x = mShowWeekNumber ? mWidth / mNumCells : 0;
+ canvas.drawLine(x, 0, mWidth, 0, mDrawPaint);
+ }
+
+ /**
+ * Draws the selected date bars if this week has a selected day.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawSelectedDateVerticalBars(Canvas canvas) {
+ if (!mHasSelectedDay) {
+ return;
+ }
+ mSelectedDateVerticalBar.setBounds(mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
+ mWeekSeperatorLineWidth,
+ mSelectedLeft + mSelectedDateVerticalBarWidth / 2, mHeight);
+ mSelectedDateVerticalBar.draw(canvas);
+ mSelectedDateVerticalBar.setBounds(mSelectedRight - mSelectedDateVerticalBarWidth / 2,
+ mWeekSeperatorLineWidth,
+ mSelectedRight + mSelectedDateVerticalBarWidth / 2, mHeight);
+ mSelectedDateVerticalBar.draw(canvas);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ mWidth = w;
+ updateSelectionPositions();
+ }
+
+ /**
+ * This calculates the positions for the selected day lines.
+ */
+ private void updateSelectionPositions() {
+ if (mHasSelectedDay) {
+ int selectedPosition = mSelectedDay - mFirstDayOfWeek;
+ if (selectedPosition < 0) {
+ selectedPosition += 7;
+ }
+ if (mShowWeekNumber) {
+ selectedPosition++;
+ }
+ mSelectedLeft = selectedPosition * mWidth / mNumCells;
+ mSelectedRight = (selectedPosition + 1) * mWidth / mNumCells;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
+ }
+ }
+}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 668490d..493b881 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -23,56 +23,96 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
-import android.widget.NumberPicker.OnChangeListener;
+import android.widget.NumberPicker.OnValueChangedListener;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
+import java.util.TimeZone;
/**
- * A view for selecting a month / year / day based on a calendar like layout.
+ * This class is a widget for selecting a date. The date can be selected by a
+ * year, month, and day spinners or a {@link CalendarView}. The set of spinners
+ * and the calendar view are automatically synchronized. The client can
+ * customize whether only the spinners, or only the calendar view, or both to be
+ * displayed. Also the minimal and maximal date from which dates to be selected
+ * can be customized.
* <p>
* See the <a href="{@docRoot}
* resources/tutorials/views/hello-datepicker.html">Date Picker tutorial</a>.
* </p>
+ * <p>
* For a dialog using this view, see {@link android.app.DatePickerDialog}.
+ * </p>
+ *
+ * @attr ref android.R.styleable#DatePicker_startYear
+ * @attr ref android.R.styleable#DatePicker_endYear
+ * @attr ref android.R.styleable#DatePicker_maxDate
+ * @attr ref android.R.styleable#DatePicker_minDate
+ * @attr ref android.R.styleable#DatePicker_spinnersShown
+ * @attr ref android.R.styleable#DatePicker_calendarViewShown
*/
@Widget
public class DatePicker extends FrameLayout {
+ private static final String LOG_TAG = DatePicker.class.getSimpleName();
+
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
+
private static final int DEFAULT_START_YEAR = 1900;
private static final int DEFAULT_END_YEAR = 2100;
- private final NumberPicker mDayPicker;
+ private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
+
+ private static final boolean DEFAULT_SPINNERS_SHOWN = true;
- private final NumberPicker mMonthPicker;
+ private final NumberPicker mDaySpinner;
- private final NumberPicker mYearPicker;
+ private final LinearLayout mSpinners;
- private final DayPicker mMiniMonthDayPicker;
+ private final NumberPicker mMonthSpinner;
+
+ private final NumberPicker mYearSpinner;
+
+ private final CalendarView mCalendarView;
private OnDateChangedListener mOnDateChangedListener;
private Locale mMonthLocale;
- private final Calendar mTempCalendar = Calendar.getInstance();
+ private final Calendar mTempDate = Calendar.getInstance();
- private final int mNumberOfMonths = mTempCalendar.getActualMaximum(Calendar.MONTH) + 1;
+ private final int mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
private final String[] mShortMonths = new String[mNumberOfMonths];
+ private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+ private final Calendar mMinDate = Calendar.getInstance();
+
+ private final Calendar mMaxDate = Calendar.getInstance();
+
+ private final Calendar mCurrentDate = Calendar.getInstance();
+
+ private boolean mIsEnabled;
+
/**
- * The callback used to indicate the user changes the date.
+ * The callback used to indicate the user changes\d the date.
*/
public interface OnDateChangedListener {
/**
+ * Called upon a date change.
+ *
* @param view The view associated with this listener.
* @param year The year that was set.
* @param monthOfYear The month that was set (0-11) for compatibility
@@ -93,103 +133,227 @@ public class DatePicker extends FrameLayout {
public DatePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker);
+ boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
+ DEFAULT_SPINNERS_SHOWN);
+ boolean calendarViewShown = attributesArray.getBoolean(
+ R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
+ int startYear = attributesArray
+ .getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+ int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+ String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
+ String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
+ attributesArray.recycle();
+
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.date_picker, this, true);
- OnChangeListener onChangeListener = new OnChangeListener() {
- public void onChange(NumberPicker picker, int oldVal, int newVal) {
- updateDateUnchecked(mYearPicker.getCurrent(), mMonthPicker.getCurrent(),
- mDayPicker.getCurrent());
+ OnValueChangedListener onChangeListener = new OnValueChangedListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ updateDate(mYearSpinner.getValue(), mMonthSpinner.getValue(), mDaySpinner
+ .getValue());
}
};
- // mini-month day-picker
- mMiniMonthDayPicker = (DayPicker) findViewById(R.id.mini_month_day_picker);
- mMiniMonthDayPicker.setOnDateChangeListener(new DayPicker.OnSelectedDayChangeListener() {
- public void onSelectedDayChange(DayPicker view, int year, int month, int monthDay) {
- updateDateUnchecked(year, month, monthDay);
+ mSpinners = (LinearLayout) findViewById(R.id.pickers);
+
+ // calendar view day-picker
+ mCalendarView = (CalendarView) findViewById(R.id.calendar_view);
+ mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
+ public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
+ updateDate(year, month, monthDay);
}
});
// day
- mDayPicker = (NumberPicker) findViewById(R.id.day);
- mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
- mDayPicker.setOnLongPressUpdateInterval(100);
- mDayPicker.setOnChangeListener(onChangeListener);
+ mDaySpinner = (NumberPicker) findViewById(R.id.day);
+ mDaySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ mDaySpinner.setOnLongPressUpdateInterval(100);
+ mDaySpinner.setOnValueChangedListener(onChangeListener);
// month
- mMonthPicker = (NumberPicker) findViewById(R.id.month);
- mMonthPicker.setRange(0, mNumberOfMonths - 1, getShortMonths());
- mMonthPicker.setOnLongPressUpdateInterval(200);
- mMonthPicker.setOnChangeListener(onChangeListener);
+ mMonthSpinner = (NumberPicker) findViewById(R.id.month);
+ mMonthSpinner.setMinValue(0);
+ mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
+ mMonthSpinner.setDisplayedValues(getShortMonths());
+ mMonthSpinner.setOnLongPressUpdateInterval(200);
+ mMonthSpinner.setOnValueChangedListener(onChangeListener);
// year
- mYearPicker = (NumberPicker) findViewById(R.id.year);
- mYearPicker.setOnLongPressUpdateInterval(100);
- mYearPicker.setOnChangeListener(onChangeListener);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker);
- int startYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
- int endYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
- setRange(startYear, endYear);
- a.recycle();
-
- // initialize to current date
- mTempCalendar.setTimeInMillis(System.currentTimeMillis());
- init(mTempCalendar.get(Calendar.YEAR), mTempCalendar.get(Calendar.MONTH),
- mTempCalendar.get(Calendar.DAY_OF_MONTH), null);
-
- // re-order the number pickers to match the current date format
- reorderPickers();
+ mYearSpinner = (NumberPicker) findViewById(R.id.year);
+ mYearSpinner.setOnLongPressUpdateInterval(100);
+ mYearSpinner.setOnValueChangedListener(onChangeListener);
+
+ // show only what the user required but make sure we
+ // show something and the spinners have higher priority
+ if (!spinnersShown && !calendarViewShown) {
+ setSpinnersShown(true);
+ } else {
+ setSpinnersShown(spinnersShown);
+ setCalendarViewShown(calendarViewShown);
+
+ // set the min date giving priority of the minDate over startYear
+ mTempDate.clear();
+ if (!TextUtils.isEmpty(minDate)) {
+ if (!parseDate(minDate, mTempDate)) {
+ mTempDate.set(startYear, 0, 1);
+ }
+ } else {
+ mTempDate.set(startYear, 0, 1);
+ }
+ mMinDate.clear();
+ setMinDate(mTempDate.getTimeInMillis());
+
+ // set the max date giving priority of the minDate over startYear
+ mTempDate.clear();
+ if (!TextUtils.isEmpty(maxDate)) {
+ if (!parseDate(maxDate, mTempDate)) {
+ mTempDate.set(endYear, 11, 31);
+ }
+ } else {
+ mTempDate.set(endYear, 11, 31);
+ }
+ mMaxDate.clear();
+ setMaxDate(mTempDate.getTimeInMillis());
+
+ // initialize to current date
+ mCurrentDate.setTimeInMillis(System.currentTimeMillis());
+ init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
+ .get(Calendar.DAY_OF_MONTH), null);
+ }
+
+ // re-order the number spinners to match the current date format
+ reorderSpinners();
+ }
+
+ /**
+ * Gets the minimal date supported by this {@link DatePicker} in
+ * milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ * <p>
+ * Note: The default minimal date is 01/01/1900.
+ * <p>
+ *
+ * @return The minimal supported date.
+ */
+ public long getMinDate() {
+ return mCalendarView.getMinDate();
+ }
+
+ /**
+ * Sets the minimal date supported by this {@link NumberPicker} in
+ * milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ *
+ * @param minDate The minimal supported date.
+ */
+ public void setMinDate(long minDate) {
+ mTempDate.setTimeInMillis(minDate);
+ if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
+ && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
+ return;
+ }
+ mMinDate.setTimeInMillis(minDate);
+ mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
+ mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
+ mCalendarView.setMinDate(minDate);
+ updateSpinners(mYearSpinner.getValue(), mMonthSpinner.getValue(), mDaySpinner.getValue());
}
/**
- * Sets the range of years in which dates can be selected.
+ * Gets the maximal date supported by this {@link DatePicker} in
+ * milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ * <p>
+ * Note: The default maximal date is 12/31/2100.
* <p>
- * Note: If the range is set to a value that does not include the currently
- * selected date the value of this picker will be updated to the closest
- * date in the range.
- * </p>
*
- * @param startYear The start year of the range.
- * @param endYear The end year of the range.
+ * @return The maximal supported date.
*/
- public void setRange(int startYear, int endYear) {
- // set ranges of the widgets
- mYearPicker.setRange(startYear, endYear);
- mTempCalendar.clear();
- Calendar startRangeDate = (Calendar) mTempCalendar.clone();
- startRangeDate.set(startYear, 0, 1);
- Calendar endRangeDate = (Calendar) mTempCalendar.clone();
- endRangeDate.set(endYear, 11, 31);
- mMiniMonthDayPicker.setRange(startRangeDate, endRangeDate);
-
- // update state if current date is outside of the range
- mTempCalendar.set(Calendar.YEAR, getYear());
- mTempCalendar.set(Calendar.MONTH, getMonth());
- mTempCalendar.set(Calendar.DAY_OF_MONTH, getDayOfMonth());
- if (mTempCalendar.before(startRangeDate)) {
- updateDate(startRangeDate.get(Calendar.YEAR), startRangeDate.get(Calendar.MONTH),
- startRangeDate.get(Calendar.DAY_OF_MONTH));
- } else if (mTempCalendar.after(endRangeDate)) {
- updateDate(endRangeDate.get(Calendar.YEAR), endRangeDate.get(Calendar.MONTH),
- endRangeDate.get(Calendar.DAY_OF_MONTH));
+ public long getMaxDate() {
+ return mCalendarView.getMaxDate();
+ }
+
+ /**
+ * Sets the maximal date supported by this {@link DatePicker} in
+ * milliseconds since January 1, 1970 00:00:00 in
+ * {@link TimeZone#getDefault()} time zone.
+ *
+ * @param maxDate The maximal supported date.
+ */
+ public void setMaxDate(long maxDate) {
+ mTempDate.setTimeInMillis(maxDate);
+ if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
+ && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
+ return;
}
+ mMaxDate.setTimeInMillis(maxDate);
+ mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
+ mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
+ mCalendarView.setMaxDate(maxDate);
+ updateSpinners(mYearSpinner.getValue(), mMonthSpinner.getValue(), mDaySpinner.getValue());
}
@Override
public void setEnabled(boolean enabled) {
+ if (mIsEnabled == enabled) {
+ return;
+ }
super.setEnabled(enabled);
- mDayPicker.setEnabled(enabled);
- mMonthPicker.setEnabled(enabled);
- mYearPicker.setEnabled(enabled);
- mMiniMonthDayPicker.setEnabled(enabled);
+ mDaySpinner.setEnabled(enabled);
+ mMonthSpinner.setEnabled(enabled);
+ mYearSpinner.setEnabled(enabled);
+ mCalendarView.setEnabled(enabled);
+ mIsEnabled = enabled;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
+ * Gets whether the {@link CalendarView} is shown.
+ *
+ * @return True if the calendar view is shown.
+ */
+ public boolean getCalendarViewShown() {
+ return mCalendarView.isShown();
+ }
+
+ /**
+ * Sets whether the {@link CalendarView} is shown.
+ *
+ * @param shown True if the calendar view is to be shown.
+ */
+ public void setCalendarViewShown(boolean shown) {
+ mCalendarView.setVisibility(shown ? VISIBLE : GONE);
+ }
+
+ /**
+ * Gets whether the spinners are shown.
+ *
+ * @return True if the spinners are shown.
+ */
+ public boolean getSpinnersShown() {
+ return mSpinners.isShown();
+ }
+
+ /**
+ * Sets whether the spinners are shown.
+ *
+ * @param shown True if the spinners are to be shown.
+ */
+ public void setSpinnersShown(boolean shown) {
+ mSpinners.setVisibility(shown ? VISIBLE : GONE);
}
/**
- * Reorders the pickers according to the date format in the current locale.
+ * Reorders the spinners according to the date format in the current
+ * {@link Locale}.
*/
- private void reorderPickers() {
+ private void reorderSpinners() {
java.text.DateFormat format;
String order;
@@ -214,10 +378,10 @@ public class DatePicker extends FrameLayout {
}
/*
- * Remove the 3 pickers from their parent and then add them back in the
+ * Remove the 3 spinners from their parent and then add them back in the
* required order.
*/
- LinearLayout parent = (LinearLayout) findViewById(R.id.pickers);
+ LinearLayout parent = mSpinners;
parent.removeAllViews();
boolean quoted = false;
@@ -232,13 +396,13 @@ public class DatePicker extends FrameLayout {
if (!quoted) {
if (c == DateFormat.DATE && !didDay) {
- parent.addView(mDayPicker);
+ parent.addView(mDaySpinner);
didDay = true;
} else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) {
- parent.addView(mMonthPicker);
+ parent.addView(mMonthSpinner);
didMonth = true;
} else if (c == DateFormat.YEAR && !didYear) {
- parent.addView(mYearPicker);
+ parent.addView(mYearSpinner);
didYear = true;
}
}
@@ -246,13 +410,13 @@ public class DatePicker extends FrameLayout {
// Shouldn't happen, but just in case.
if (!didMonth) {
- parent.addView(mMonthPicker);
+ parent.addView(mMonthSpinner);
}
if (!didDay) {
- parent.addView(mDayPicker);
+ parent.addView(mDaySpinner);
}
if (!didYear) {
- parent.addView(mYearPicker);
+ parent.addView(mYearSpinner);
}
}
@@ -264,10 +428,12 @@ public class DatePicker extends FrameLayout {
* @param dayOfMonth The day of the month.
*/
public void updateDate(int year, int month, int dayOfMonth) {
- if (mYearPicker.getCurrent() != year
- || mDayPicker.getCurrent() != dayOfMonth
- || mMonthPicker.getCurrent() != month) {
- updateDateUnchecked(year, month, dayOfMonth);
+ if (mCurrentDate.get(Calendar.YEAR) != year
+ || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
+ || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month) {
+ updateSpinners(year, month, dayOfMonth);
+ updateCalendarView();
+ notifyDateChanged();
}
}
@@ -280,20 +446,20 @@ public class DatePicker extends FrameLayout {
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
- return new SavedState(superState, mYearPicker.getCurrent(), mMonthPicker.getCurrent(),
- mDayPicker.getCurrent());
+ return new SavedState(superState, mYearSpinner.getValue(), mMonthSpinner.getValue(),
+ mDaySpinner.getValue());
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
- updatePickers(ss.mYear, ss.mMonth, ss.mDay);
+ updateSpinners(ss.mYear, ss.mMonth, ss.mDay);
}
/**
* Initialize the state. If the provided values designate an inconsistent
- * date the values are normalized before updating the pickers.
+ * date the values are normalized before updating the spinners.
*
* @param year The initial year.
* @param monthOfYear The initial month <strong>starting from zero</strong>.
@@ -308,16 +474,19 @@ public class DatePicker extends FrameLayout {
}
/**
- * Updates the current date.
+ * Parses the given <code>date</code> and in case of success sets the result
+ * to the <code>outDate</code>.
*
- * @param year The year.
- * @param month The month which is <strong>starting from zero</strong>.
- * @param dayOfMonth The day of the month.
+ * @return True if the date was parsed.
*/
- private void updateDateUnchecked(int year, int month, int dayOfMonth) {
- updatePickers(year, month, dayOfMonth);
- updateMiniMonth();
- notifyDateChanged();
+ private boolean parseDate(String date, Calendar outDate) {
+ try {
+ outDate.setTime(mDateFormat.parse(date));
+ return true;
+ } catch (ParseException e) {
+ Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
+ return false;
+ }
}
/**
@@ -338,33 +507,80 @@ public class DatePicker extends FrameLayout {
}
/**
- * Updates the pickers with the given <code>year</code>, <code>month</code>,
- * and <code>dayOfMonth</code>. If the provided values designate an inconsistent
- * date the values are normalized before updating the pickers.
+ * Updates the spinners with the given <code>year</code>, <code>month</code>
+ * , and <code>dayOfMonth</code>. If the provided values designate an
+ * inconsistent date the values are normalized before updating the spinners.
+ */
+ private void updateSpinners(int year, int month, int dayOfMonth) {
+ mCurrentDate.set(Calendar.YEAR, year);
+ int deltaMonths = getDelataMonth(month);
+ mCurrentDate.add(Calendar.MONTH, deltaMonths);
+ int deltaDays = getDelataDayOfMonth(dayOfMonth);
+ mCurrentDate.add(Calendar.DAY_OF_MONTH, deltaDays);
+
+ if (mCurrentDate.before(mMinDate)) {
+ mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
+ } else if (mCurrentDate.after(mMaxDate)) {
+ mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
+ }
+
+ mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
+ mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
+ mDaySpinner.setMinValue(1);
+ mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
+ mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+ }
+
+ /**
+ * @return The delta days of moth from the current date and the given
+ * <code>dayOfMonth</code>.
*/
- private void updatePickers(int year, int month, int dayOfMonth) {
- // larger fields are not updated and the day is adjusted without wrapping
- mTempCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
- mTempCalendar.roll(Calendar.MONTH, month - mTempCalendar.get(Calendar.MONTH));
- mTempCalendar.roll(Calendar.YEAR, year - mTempCalendar.get(Calendar.YEAR));
-
- mYearPicker.setCurrent(mTempCalendar.get(Calendar.YEAR));
- mMonthPicker.setCurrent(mTempCalendar.get(Calendar.MONTH));
- mDayPicker.setRange(1, mTempCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
- mDayPicker.setCurrent(mTempCalendar.get(Calendar.DAY_OF_MONTH));
+ private int getDelataDayOfMonth(int dayOfMonth) {
+ int prevDayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+ if (prevDayOfMonth == dayOfMonth) {
+ return 0;
+ }
+ int maxDayOfMonth = mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH);
+ if (dayOfMonth == 1 && prevDayOfMonth == maxDayOfMonth) {
+ return 1;
+ }
+ if (dayOfMonth == maxDayOfMonth && prevDayOfMonth == 1) {
+ return -1;
+ }
+ return dayOfMonth - prevDayOfMonth;
+ }
+
+ /**
+ * @return The delta months from the current date and the given
+ * <code>month</code>.
+ */
+ private int getDelataMonth(int month) {
+ int prevMonth = mCurrentDate.get(Calendar.MONTH);
+ if (prevMonth == month) {
+ return 0;
+ }
+ if (month == 0 && prevMonth == 11) {
+ return 1;
+ }
+ if (month == 11 && prevMonth == 0) {
+ return -1;
+ }
+ return month - prevMonth;
}
/**
- * Updates the mini-month with the given year, month, and day selected by the
- * number pickers.
+ * Updates the calendar view with the given year, month, and day selected by
+ * the number spinners.
*/
- private void updateMiniMonth() {
- Calendar selectedDay = mMiniMonthDayPicker.getSelectedDay();
- if (selectedDay.get(Calendar.YEAR) != mYearPicker.getCurrent()
- || selectedDay.get(Calendar.MONTH) != mMonthPicker.getCurrent()
- || selectedDay.get(Calendar.DAY_OF_MONTH) != mDayPicker.getCurrent()) {
- mMiniMonthDayPicker.goTo(mYearPicker.getCurrent(), mMonthPicker.getCurrent(),
- mDayPicker.getCurrent(), false, true, false);
+ private void updateCalendarView() {
+ mTempDate.setTimeInMillis(mCalendarView.getDate());
+ if (mTempDate.get(Calendar.YEAR) != mYearSpinner.getValue()
+ || mTempDate.get(Calendar.MONTH) != mMonthSpinner.getValue()
+ || mTempDate.get(Calendar.DAY_OF_MONTH) != mDaySpinner.getValue()) {
+ mTempDate.clear();
+ mTempDate.set(mYearSpinner.getValue(), mMonthSpinner.getValue(),
+ mDaySpinner.getValue());
+ mCalendarView.setDate(mTempDate.getTimeInMillis(), false, false);
}
}
@@ -372,21 +588,21 @@ public class DatePicker extends FrameLayout {
* @return The selected year.
*/
public int getYear() {
- return mYearPicker.getCurrent();
+ return mYearSpinner.getValue();
}
/**
* @return The selected month.
*/
public int getMonth() {
- return mMonthPicker.getCurrent();
+ return mMonthSpinner.getValue();
}
/**
* @return The selected day of month.
*/
public int getDayOfMonth() {
- return mDayPicker.getCurrent();
+ return mDaySpinner.getValue();
}
/**
@@ -394,8 +610,8 @@ public class DatePicker extends FrameLayout {
*/
private void notifyDateChanged() {
if (mOnDateChangedListener != null) {
- mOnDateChangedListener.onDateChanged(DatePicker.this, mYearPicker.getCurrent(),
- mMonthPicker.getCurrent(), mDayPicker.getCurrent());
+ mOnDateChangedListener.onDateChanged(DatePicker.this, mYearSpinner.getValue(),
+ mMonthSpinner.getValue(), mDaySpinner.getValue());
}
}
diff --git a/core/java/android/widget/DayPicker.java b/core/java/android/widget/DayPicker.java
deleted file mode 100644
index 02805be..0000000
--- a/core/java/android/widget/DayPicker.java
+++ /dev/null
@@ -1,1532 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import com.android.internal.R;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.Style;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView.OnScrollListener;
-
-import java.security.InvalidParameterException;
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.Locale;
-
-import libcore.icu.LocaleData;
-
-/**
- * Displays a day picker in the form of a calendar. The calendar
- * is represented as a list where each row depicts a week. Each week is
- * composed of items that are selectable days.
- */
-public class DayPicker extends FrameLayout {
-
- /**
- * The number of milliseconds in a day.
- *
- * @hide
- */
- protected static final long MILLIS_IN_DAY = 86400000L;
-
- /**
- * The number of day in a week.
- *
- * @hide
- */
- protected static final int DAYS_PER_WEEK = 7;
-
- /**
- * The number of milliseconds in a week.
- *
- * @hide
- */
- protected static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
-
- /**
- * Affects when the month selection will change while scrolling up
- *
- * @hide
- */
- protected static final int SCROLL_HYST_WEEKS = 2;
-
- /**
- * How long the GoTo fling animation should last.
- *
- * @hide
- */
- protected static final int GOTO_SCROLL_DURATION = 1000;
-
- /**
- * The duration of the adjustment upon a user scroll in milliseconds.
- *
- * @hide
- */
- protected static final int ADJUSTMENT_SCROLL_DURATION = 500;
-
- /**
- * How long to wait after receiving an onScrollStateChanged notification
- * before acting on it.
- *
- * @hide
- */
- protected static final int SCROLL_CHANGE_DELAY = 40;
-
- /**
- * The scale used to compensate for different screen density.
- *
- * @hide
- */
- protected static float sScale;
-
- /**
- * The top offset of the weeks list.
- *
- * @hide
- */
- protected static int mListTopOffset = 2;
-
- /**
- * The visible height of a week view.
- *
- * @hide
- */
- protected int mWeekMinVisibleHeight = 12;
-
-
- /**
- * The visible height of a week view.
- *
- * @hide
- */
- protected int mBottomBuffer = 20;
-
- /**
- * The number of shown weeks.
- *
- * @hide
- */
- protected int mShownWeekCount = 6;
-
- /**
- * Flag whether to show the week number.
- *
- * @hide
- */
- protected boolean mShowWeekNumber = true;
-
- /**
- * The number of day per week to be shown
- *
- * @hide
- */
- protected int mDaysPerWeek = 7;
-
- /**
- * The friction of the week list while flinging.
- *
- * @hide
- */
- protected float mFriction = .05f;
-
- /**
- * Scale for adjusting velocity of the week list while flinging.
- *
- * @hide
- */
- protected float mVelocityScale = 0.333f;
-
- /**
- * The adapter for the weeks list.
- *
- * @hide
- */
- protected WeeksAdapter mAdapter;
-
- /**
- * The weeks list.
- *
- * @hide
- */
- protected ListView mListView;
-
- /**
- * The name of the month to display.
- *
- * @hide
- */
- protected TextView mMonthName;
-
- /**
- * The header with week day names.
- *
- * @hide
- */
- protected ViewGroup mDayNamesHeader;
-
- /**
- * Cached labels for the week names header.
- *
- * @hide
- */
- protected String[] mDayLabels;
-
- /**
- * Temporary instance to avoid multiple instantiations.
- *
- * @hide
- */
- protected Calendar mTempCalendar = Calendar.getInstance();
-
- /**
- * The first day of the week based on the current locale.
- *
- * @hide
- */
- protected int mFirstDayOfWeek = LocaleData.get(Locale.getDefault()).firstDayOfWeek;
-
- /**
- * The first day of the focused month.
- *
- * @hide
- */
- protected Calendar mFirstDayOfMonth = Calendar.getInstance();
-
- /**
- * Which month should be displayed/highlighted [0-11]
- *
- * @hide
- */
- protected int mCurrentMonthDisplayed;
-
- /**
- * Used for tracking during a scroll.
- *
- * @hide
- */
- protected long mPreviousScrollPosition;
-
- /**
- * Used for tracking which direction the view is scrolling.
- *
- * @hide
- */
- protected boolean mIsScrollingUp = false;
-
- /**
- * The previous scroll state of the weeks ListView.
- *
- * @hide
- */
- protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- /**
- * The current scroll state of the weeks ListView.
- *
- * @hide
- */
- protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- /**
- * Listener for changes in the selected day.
- *
- * @hide
- */
- protected OnSelectedDayChangeListener mOnChangeListener;
-
- /**
- * Command for adjusting the position after a scroll/fling.
- *
- * @hide
- */
- protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
-
- /**
- * The start date of the range supported by this picker.
- *
- * @hide
- */
- protected Calendar mRangeStartDate = Calendar.getInstance();
-
- /**
- * The end date of the range supported by this picker.
- *
- * @hide
- */
- protected Calendar mRangeEndDate = Calendar.getInstance();
-
- /**
- * String for formatting the month name in the title text view.
- *
- * @hide
- */
- protected String mMonthNameFormatSrting = "MMMM, yyyy";
-
- /**
- * The callback used to indicate the user changes the date.
- */
- public interface OnSelectedDayChangeListener {
-
- /**
- * Called upon change of the selected day.
- *
- * @param view The view associated with this listener.
- * @param year The year that was set.
- * @param month The month that was set [0-11].
- * @param dayOfMonth The day of the month that was set.
- */
- public void onSelectedDayChange(DayPicker view, int year, int month, int dayOfMonth);
- }
-
- public DayPicker(Context context) {
- this(context, null);
- }
-
- public DayPicker(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DayPicker(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, 0);
-
- LayoutInflater layoutInflater = (LayoutInflater) mContext
- .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
- View content = layoutInflater.inflate(R.layout.day_picker, null, false);
- addView(content);
-
- mListView = (ListView) findViewById(R.id.list);
- mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
- mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
-
- // Adjust sizes for screen density
- if (sScale == 0) {
- sScale = mContext.getResources().getDisplayMetrics().density;
- if (sScale != 1) {
- mWeekMinVisibleHeight *= sScale;
- mBottomBuffer *= sScale;
- mListTopOffset *= sScale;
- }
- }
-
- // set default range
- mRangeStartDate.clear();
- mRangeStartDate.set(1900, 0, 1);
- mRangeEndDate.clear();
- mRangeEndDate.set(2100, 0, 1);
-
- setUpHeader();
- updateHeader();
- setUpListView();
- setUpAdapter();
-
- // go to today now
- mTempCalendar.setTimeInMillis(System.currentTimeMillis());
- goTo(mTempCalendar, false, true, true);
- invalidate();
- }
-
- /**
- * Sets the range supported by this day picker. This is the picker will not
- * support dates before <code>startRangeDate</code> and <code>endRangeDate
- * </code>.
- *
- * @param startRangeDate The start date.
- * @param endRangeDate The end date.
- */
- public void setRange(Calendar startRangeDate, Calendar endRangeDate) {
- boolean rangeChanged = false;
- if (mRangeStartDate.get(Calendar.DAY_OF_YEAR) != startRangeDate.get(Calendar.DAY_OF_YEAR)
- || mRangeStartDate.get(Calendar.YEAR) != startRangeDate.get(Calendar.YEAR)) {
- mRangeStartDate.setTimeInMillis(startRangeDate.getTimeInMillis());
- mRangeStartDate.setTimeZone(startRangeDate.getTimeZone());
- rangeChanged = true;
- }
- if (mRangeEndDate.get(Calendar.DAY_OF_YEAR) != endRangeDate.get(Calendar.DAY_OF_YEAR)
- || mRangeEndDate.get(Calendar.YEAR) != endRangeDate.get(Calendar.YEAR)) {
- mRangeEndDate.setTimeInMillis(endRangeDate.getTimeInMillis());
- mRangeEndDate.setTimeZone(endRangeDate.getTimeZone());
- rangeChanged = true;
- }
-
- if (!rangeChanged) {
- return;
- }
-
- // now recreate the adapter since we have a new range to handle
- mAdapter = null;
- setUpAdapter();
-
- // set the current date to today if in the range
- // otherwise to the closest end of the range
- mTempCalendar.clear();
- mTempCalendar.setTimeInMillis(System.currentTimeMillis());
- if (mTempCalendar.before(mRangeStartDate)) {
- goTo(mRangeStartDate, false, true, true);
- } else if (mTempCalendar.after(mRangeEndDate)) {
- goTo(mRangeEndDate, false, true, true);
- } else {
- goTo(mTempCalendar, false, true, true);
- }
- }
-
- /**
- * Sets the listener to be notified upon day selection changes.
- *
- * @param listener The listener to be called back.
- */
- public void setOnDateChangeListener(OnSelectedDayChangeListener listener) {
- mOnChangeListener = listener;
- }
-
- /**
- * Gets the selected day.
- *
- * @return The selected day.
- */
- public Calendar getSelectedDay() {
- return (Calendar) mAdapter.mSelectedDay.clone();
- }
-
- /**
- * Sets the selected day. This is equivalent to a call to
- * {@link #goTo(Calendar, boolean, boolean, boolean)} with
- * the arguments <code>selectedDay</code>, <code>false</code>,
- * <code>true</code>, <code>false</code> respectively.
- *
- * @param selectedDay The selected day.
- */
- public void setSelectedDay(Calendar selectedDay) {
- goTo(selectedDay, false, true, false);
- }
-
- /**
- * Creates a new adapter if necessary and sets up its parameters. Override
- * this method to provide a custom adapter.
- *
- * @hide
- */
- protected void setUpAdapter() {
- if (mAdapter == null) {
- mAdapter = new WeeksAdapter(getContext());
- mAdapter.registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- if (mOnChangeListener != null) {
- Calendar selectedDay = mAdapter.getSelectedDay();
- mOnChangeListener.onSelectedDayChange(DayPicker.this,
- selectedDay.get(Calendar.YEAR),
- selectedDay.get(Calendar.MONTH),
- selectedDay.get(Calendar.DAY_OF_MONTH));
- }
- }
- });
- mListView.setAdapter(mAdapter);
- }
-
- // refresh the view with the new parameters
- mAdapter.notifyDataSetChanged();
- }
-
- /**
- * Sets up the strings to be used by the header. Override this method to use
- * different strings or modify the view params.
- *
- * @hide
- */
- protected void setUpHeader() {
- mDayLabels = new String[mDaysPerWeek];
- for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
- int calendarDay = (i < mDaysPerWeek) ? i : 1; // Calendar.MONDAY is
- // 1
- mDayLabels[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
- DateUtils.LENGTH_SHORTEST);
- }
- }
-
- /**
- * Sets all the required fields for the list view. Override this method to
- * set a different list view behavior.
- *
- * @hide
- */
- protected void setUpListView() {
- // Configure the listview
- mListView.setDivider(null);
- mListView.setItemsCanFocus(true);
- mListView.setVerticalScrollBarEnabled(false);
- mListView.setOnScrollListener(new OnScrollListener() {
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- DayPicker.this.onScrollStateChanged(view, scrollState);
- }
-
- public void onScroll(
- AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- DayPicker.this.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
- }
- });
- // Make the scrolling behavior nicer
- mListView.setFriction(mFriction);
- mListView.setVelocityScale(mVelocityScale);
- }
-
- /**
- * Fixes the day names header to provide correct spacing and updates the
- * label text. Override this to set up a custom header.
- *
- * @hide
- */
- protected void updateHeader() {
- TextView label = (TextView) mDayNamesHeader.getChildAt(0);
- if (mShowWeekNumber) {
- label.setVisibility(View.VISIBLE);
- } else {
- label.setVisibility(View.GONE);
- }
- for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
- label = (TextView) mDayNamesHeader.getChildAt(i);
- if (i < mDaysPerWeek + 1) {
- label.setText(mDayLabels[i - 1]);
- label.setVisibility(View.VISIBLE);
- } else {
- label.setVisibility(View.GONE);
- }
- }
- mDayNamesHeader.invalidate();
- }
-
- /**
- * This moves to the specified time in the view. If the time is not already
- * in range it will move the list so that the first of the month containing
- * the time is at the top of the view. If the new time is already in view
- * the list will not be scrolled unless forceScroll is true. This time may
- * optionally be highlighted as selected as well.
- *
- * @param year The year to move to.
- * @param month The month to move to <strong>starting from zero<strong>.
- * @param dayOfMonth The month day to move to.
- * @param animate Whether to scroll to the given time or just redraw at the
- * new location.
- * @param setSelected Whether to set the given time as selected
- * @param forceScroll Whether to recenter even if the time is already
- * visible.
- *
- * @throws IllegalArgumentException of the provided date is before the
- * range start of after the range end.
- *
- * @see #setRange(Calendar, Calendar)
- */
- public void goTo(int year, int month, int dayOfMonth, boolean animate, boolean setSelected,
- boolean forceScroll) {
- mTempCalendar.clear();
- mTempCalendar.set(year, month, dayOfMonth);
- goTo(mTempCalendar, animate, setSelected, forceScroll);
- }
-
- /**
- * This moves to the specified time in the view. If the time is not already
- * in range it will move the list so that the first of the month containing
- * the time is at the top of the view. If the new time is already in view
- * the list will not be scrolled unless forceScroll is true. This time may
- * optionally be highlighted as selected as well.
- *
- * @param date The time to move to.
- * @param animate Whether to scroll to the given time or just redraw at the
- * new location.
- * @param setSelected Whether to set the given time as selected.
- * @param forceScroll Whether to recenter even if the time is already
- * visible.
- *
- * @throws IllegalArgumentException of the provided date is before the
- * range start of after the range end.
- *
- * @see #setRange(Calendar, Calendar)
- */
- public void goTo(Calendar date, boolean animate, boolean setSelected, boolean forceScroll) {
- if (date.before(mRangeStartDate) || date.after(mRangeEndDate)) {
- throw new IllegalArgumentException("Time not between " + mRangeStartDate.getTime()
- + " and " + mRangeEndDate.getTime());
- }
- // Find the first and last entirely visible weeks
- int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
- View firstChild = mListView.getChildAt(0);
- if (firstChild != null && firstChild.getTop() < 0) {
- firstFullyVisiblePosition++;
- }
- int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
- if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
- lastFullyVisiblePosition--;
- }
- if (setSelected) {
- mAdapter.setSelectedDay(date);
- }
- // Get the week we're going to
- int position = getWeeksDelta(date);
-
- // Check if the selected day is now outside of our visible range
- // and if so scroll to the month that contains it
- if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
- || forceScroll) {
- mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
- mFirstDayOfMonth.setTimeZone(date.getTimeZone());
- mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
-
- setMonthDisplayed(mFirstDayOfMonth);
- position = getWeeksDelta(mFirstDayOfMonth);
-
- mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
- if (animate) {
- mListView.smoothScrollToPositionFromTop(position, mListTopOffset,
- GOTO_SCROLL_DURATION);
- } else {
- mListView.setSelectionFromTop(position, mListTopOffset);
- // Perform any after scroll operations that are needed
- onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
- }
- } else if (setSelected) {
- // Otherwise just set the selection
- setMonthDisplayed(date);
- }
- }
-
- /**
- * Called when a <code>view</code> transitions to a new <code>scrollState
- * </code>.
- *
- * @hide
- */
- protected void onScrollStateChanged(AbsListView view, int scrollState) {
- mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
- }
-
- /**
- * Updates the title and selected month if the <code>view</code> has moved to a new
- * month.
- *
- * @hide
- */
- protected void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- WeekView child = (WeekView) view.getChildAt(0);
- if (child == null) {
- return;
- }
-
- // Figure out where we are
- long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
-
- // If we have moved since our last call update the direction
- if (currScroll < mPreviousScrollPosition) {
- mIsScrollingUp = true;
- } else if (currScroll > mPreviousScrollPosition) {
- mIsScrollingUp = false;
- } else {
- return;
- }
-
- // Use some hysteresis for checking which month to highlight. This
- // causes the month to transition when two full weeks of a month are
- // visible when scrolling up, and when the first day in a month reaches
- // the top of the screen when scrolling down.
- int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
- if (mIsScrollingUp) {
- child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
- } else if (offset != 0) {
- child = (WeekView) view.getChildAt(offset);
- }
-
- // Find out which month we're moving into
- int month;
- if (mIsScrollingUp) {
- month = child.getMonthOfFirstWeekDay();
- } else {
- month = child.getMonthOfLastWeekDay();
- }
-
- // And how it relates to our current highlighted month
- int monthDiff;
- if (mCurrentMonthDisplayed == 11 && month == 0) {
- monthDiff = 1;
- } else if (mCurrentMonthDisplayed == 0 && month == 11) {
- monthDiff = -1;
- } else {
- monthDiff = month - mCurrentMonthDisplayed;
- }
-
- // Only switch months if we're scrolling away from the currently
- // selected month
- if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
- Calendar firstDay = child.getFirstDay();
- if (mIsScrollingUp) {
- firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
- } else {
- firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
- }
- setMonthDisplayed(firstDay);
- }
- mPreviousScrollPosition = currScroll;
- mPreviousScrollState = mCurrentScrollState;
- }
-
- /**
- * Sets the month displayed at the top of this view based on time. Override
- * to add custom events when the title is changed.
- *
- * @param calendar A day in the new focus month.
- *
- * @hide
- */
- protected void setMonthDisplayed(Calendar calendar) {
- mMonthName.setText(DateFormat.format(mMonthNameFormatSrting, calendar));
- mMonthName.invalidate();
- mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
- mAdapter.setFocusMonth(mCurrentMonthDisplayed);
- // TODO Send Accessibility Event
- }
-
- /**
- * @return Returns the number of weeks between the current week day of the
- * <code>fromDate</code> and the first day of week of
- * <code>toDate</code>.
- *
- * @hide
- */
- protected int getWeeksDelta(Calendar toDate) {
- if (toDate.before(mRangeStartDate)) {
- throw new IllegalArgumentException("fromDate: " + mRangeStartDate.getTime()
- + " does not precede toDate: " + toDate.getTime());
- }
-
- int fromDateDayOfWeek = mRangeStartDate.get(Calendar.DAY_OF_WEEK);
- long diff = (fromDateDayOfWeek - mFirstDayOfWeek) * MILLIS_IN_DAY;
- if (diff < 0) {
- diff = diff + MILLIS_IN_WEEK;
- }
- long refDay = mRangeStartDate.getTimeInMillis() - diff;
- return (int) ((toDate.getTimeInMillis() - refDay) / MILLIS_IN_WEEK);
- }
-
- /**
- * Command responsible for acting upon scroll state changes.
- *
- * @hide
- */
- protected class ScrollStateRunnable implements Runnable {
- private AbsListView mView;
-
- private int mNewState;
-
- /**
- * Sets up the runnable with a short delay in case the scroll state
- * immediately changes again.
- *
- * @param view The list view that changed state
- * @param scrollState The new state it changed to
- */
- public void doScrollStateChange(AbsListView view, int scrollState) {
- removeCallbacks(this);
- mView = view;
- mNewState = scrollState;
- removeCallbacks(this);
- postDelayed(this, SCROLL_CHANGE_DELAY);
- }
-
- public void run() {
- mCurrentScrollState = mNewState;
- // Fix the position after a scroll or a fling ends
- if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
- mPreviousScrollState = mNewState;
- View child = mView.getChildAt(0);
- if (child == null) {
- // The view is no longer visible, just return
- return;
- }
- int dist = child.getBottom() - mListTopOffset;
- if (dist > mListTopOffset) {
- if (mIsScrollingUp) {
- mView.smoothScrollBy(dist - child.getHeight(), ADJUSTMENT_SCROLL_DURATION);
- } else {
- mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
- }
- }
- } else {
- mPreviousScrollState = mNewState;
- }
- }
- }
-
- /**
- * <p>
- * This is a specialized adapter for creating a list of weeks with
- * selectable days. It can be configured to display the week number, start
- * the week on a given day, show a reduced number of days, or display an
- * arbitrary number of weeks at a time.
- * </p>
- *
- * @hide
- */
- public class WeeksAdapter extends BaseAdapter implements OnTouchListener {
-
- /**
- * The default maximum year supported by the Date Time Picker.
- */
- public static final int DEFAULT_MAX_CALENDAR_YEAR = 2100;
-
- /**
- * The default minimum year supported by the Date Time Picker.
- */
- public static final int DEFAULT_MIN_CALENDAR_YEAR = 1900;
-
- /**
- * The number of weeks to display at a time.
- */
- public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks";
-
- /**
- * Which month should be in focus currently.
- */
- public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month";
-
- /**
- * Whether the week number should be shown. Non-zero to show them.
- */
- public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers";
-
- /**
- * Which day the week should start on. {@link Time#SUNDAY} through
- * {@link Time#SATURDAY}.
- */
- public static final String WEEK_PARAMS_WEEK_START = "week_start";
-
- /**
- * The year of the highlighted day.
- */
- public static final String WEEK_PARAMS_YEAR = "selected_year";
-
- /**
- * The month of the highlighted day.
- */
- public static final String WEEK_PARAMS_MONTH = "selected_month";
-
- /**
- * The year of the highlighted day.
- */
- public static final String WEEK_PARAMS_DAY_OF_MONTH = "selected_day_of_month";
-
- /**
- * The start date of the supported interval.
- */
- public static final String WEEK_PARAMS_START_DATE_RANGE_MILLIS = "start_date_gange_millis";
-
- /**
- * The end date of the supported interval.
- */
- public static final String WEEK_PARAMS_END_DATE_RANGE_MILLIS = "end_date_gange_millis";
-
- /**
- * How many days of the week to display [1-7].
- */
- public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week";
-
- protected int WEEK_7_OVERHANG_HEIGHT = 7;
-
- protected int mSelectedWeek;
-
- protected GestureDetector mGestureDetector;
-
- protected int mFocusMonth = 0;
-
- private final Calendar mSelectedDay = Calendar.getInstance();
-
- private int mTotalWeekCount = -1;
-
- public WeeksAdapter(Context context) {
- mContext = context;
-
- if (sScale == 0) {
- sScale = context.getResources().getDisplayMetrics().density;
- if (sScale != 1) {
- WEEK_7_OVERHANG_HEIGHT *= sScale;
- }
- }
- init();
- }
-
- /**
- * Set up the gesture detector and selected time
- */
- protected void init() {
- mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
- mSelectedWeek = getWeeksDelta(mSelectedDay);
-
- // make adjustment to fit the range last week with needed overflow
- mTempCalendar.setTimeInMillis(mRangeEndDate.getTimeInMillis());
- mTempCalendar.setTimeZone(mRangeEndDate.getTimeZone());
- int diff = mFirstDayOfWeek - mRangeEndDate.get(Calendar.DAY_OF_WEEK);
- if (diff < 0) {
- diff += DAYS_PER_WEEK;
- }
- mTempCalendar.add(Calendar.DAY_OF_WEEK, diff);
- mTotalWeekCount = getWeeksDelta(mTempCalendar);
- }
-
- /**
- * Updates the selected day and related parameters.
- *
- * @param selectedDay The time to highlight
- */
- public void setSelectedDay(Calendar selectedDay) {
- if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDay.get(Calendar.DAY_OF_YEAR)
- && selectedDay.get(Calendar.YEAR) == mSelectedDay.get(Calendar.YEAR)) {
- return;
- }
- mSelectedDay.setTimeInMillis(selectedDay.getTimeInMillis());
- mSelectedDay.setTimeZone(selectedDay.getTimeZone());
- mSelectedWeek = getWeeksDelta(mSelectedDay);
- mFocusMonth = mSelectedDay.get(Calendar.MONTH);
- notifyDataSetChanged();
- }
-
- /**
- * @return The selected day of month.
- */
- public Calendar getSelectedDay() {
- return mSelectedDay;
- }
-
- /**
- * updates any config options that may have changed and refreshes the
- * view
- */
- public void refresh() {
- notifyDataSetChanged();
- }
-
- @Override
- public int getCount() {
- return mTotalWeekCount;
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- WeekView v;
- HashMap<String, Object> drawingParams = null;
- if (convertView != null) {
- v = (WeekView) convertView;
- // We store the drawing parameters in the view so it can be
- // recycled
- drawingParams = (HashMap<String, Object>) v.getTag();
- } else {
- v = getNewView();
- // Set up the new view
- android.widget.AbsListView.LayoutParams params =
- new android.widget.AbsListView.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- v.setLayoutParams(params);
- v.setClickable(true);
- v.setOnTouchListener(this);
-
- drawingParams = new HashMap<String, Object>();
- }
-
- // pass in all the view parameters
- putDrawingParementer(drawingParams, WeekView.VIEW_PARAMS_SHOW_WK_NUM,
- mShowWeekNumber ? 1 : 0);
- putDrawingParementer(drawingParams, WeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek);
- putDrawingParementer(drawingParams, WeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek);
- putDrawingParementer(drawingParams, WeekView.VIEW_PARAMS_WEEK, position);
- putDrawingParementer(drawingParams, WeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth);
- putDrawingParementer(drawingParams, WeekView.VIEW_PARAMS_SELECTED_DAY,
- (mSelectedWeek == position) ? mSelectedDay.get(Calendar.DAY_OF_WEEK) : -1);
- v.setWeekParams(drawingParams);
-
- return v;
- }
-
- /**
- * Puts the given <code>value</code> for the drawing
- * <code>parameter</code> in the <code>drawingParams</code>.
- */
- private void putDrawingParementer(HashMap<String, Object> drawingParams, String parameter,
- int value) {
- int[] valueArray = (int[]) drawingParams.get(parameter);
- if (valueArray == null) {
- valueArray = new int[1];
- drawingParams.put(parameter, valueArray);
- }
- valueArray[0] = value;
- }
-
- /**
- * Creates a new WeekView and returns it. Override this to customize the
- * view creation.
- *
- * @return A new WeekView
- */
- protected WeekView getNewView() {
- return new WeekView(mContext);
- }
-
- /**
- * Changes which month is in focus and updates the view.
- *
- * @param month The month to show as in focus [0-11]
- */
- public void setFocusMonth(int month) {
- if (mFocusMonth == month) {
- return;
- }
- mFocusMonth = month;
- notifyDataSetChanged();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (mGestureDetector.onTouchEvent(event)) {
- WeekView weekView = (WeekView) v;
- weekView.getDayFromLocation(event.getX(), mTempCalendar);
- // it is possible that the touched day is outside the valid range
- // we draw whole weeks but range end can fall not on the week end
- if (mTempCalendar.before(mRangeStartDate) || mTempCalendar.after(mRangeEndDate)) {
- return true;
- }
- onDayTapped(mTempCalendar);
- return true;
- }
- return false;
- }
-
- /**
- * Maintains the same hour/min/sec but moves the day to the tapped day.
- *
- * @param day The day that was tapped
- */
- protected void onDayTapped(Calendar day) {
- setSelectedDay(day);
- setMonthDisplayed(day);
- }
-
- /**
- * This is here so we can identify single tap events and set the
- * selected day correctly
- */
- protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return true;
- }
- }
- }
-
- /**
- * <p>
- * This is a dynamic view for drawing a single week. It can be configured to
- * display the week number, start the week on a given day, or show a reduced
- * number of days. It is intended for use as a single view within a
- * ListView. See {@link WeeksAdapter} for usage.
- * </p>
- *
- * @hide
- */
- public class WeekView extends View {
-
- /*
- * These params can be passed into the view to control how it appears.
- * {@link #VIEW_PARAMS_WEEK} is the only required field, though the
- * default values are unlikely to fit most layouts correctly.
- */
-
- /**
- * This sets the height of this week in pixels
- */
- public static final String VIEW_PARAMS_HEIGHT = "height";
-
- /**
- * This specifies the position (or weeks since the epoch) of this week.
- */
- public static final String VIEW_PARAMS_WEEK = "week";
-
- /**
- * This sets one of the days in this view as selected
- * {@link Time#SUNDAY} through {@link Time#SATURDAY}.
- */
- public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day";
-
- /**
- * Which day the week should start on. {@link Time#SUNDAY} through
- * {@link Time#SATURDAY}.
- */
- public static final String VIEW_PARAMS_WEEK_START = "week_start";
-
- /**
- * How many days to display at a time. Days will be displayed starting
- * with {@link #mFirstDay}.
- */
- public static final String VIEW_PARAMS_NUM_DAYS = "num_days";
-
- /**
- * Which month is currently in focus, as defined by {@link Time#month}
- * [0-11].
- */
- public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month";
-
- /**
- * If this month should display week numbers. false if 0, true
- * otherwise.
- */
- public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num";
-
- protected static final int DEFAULT_SHOW_WK_NUM = 0;
-
- protected final int mWeekSeperatorWidth;
-
- protected final int mNumberTextSize;
-
- protected final int mWeekDayPadding;
-
- protected final Rect mTempRect = new Rect();
-
- protected final Paint mDrawPaint = new Paint();
-
- protected final Paint mMonthNumDrawPaint = new Paint();
-
- protected final Drawable mSelectedDayLine;
-
- protected final int mSelectedDayLineWidth;
-
- protected final int mSelectionBackgroundColor;
-
- protected final int mFocusedMonthDateColor;
-
- protected final int mOtherMonthDateColor;
-
- protected final int mGridLinesColor;
-
- protected final int mWeekNumberColor;
-
- protected final int mFirstDayOfweek;
-
- // Cache the number strings so we don't have to recompute them each time
- protected String[] mDayNumbers;
-
- // Quick lookup for checking which days are in the focus month
- protected boolean[] mFocusDay;
-
- // The first day displayed by this item
- protected Calendar mFirstDay;
-
- // The month of the first day in this week
- protected int mMonthOfFirstWeekDay = -1;
-
- // The month of the last day in this week
- protected int mLastWeekDayMonth = -1;
-
- // The position of this week, equivalent to weeks since the week of Jan
- // 1st, 1900
- protected int mWeek = -1;
-
- // Quick reference to the width of this view, matches parent
- protected int mWidth;
-
- // The height this view should draw at in pixels, set by height param
- protected int mHeight;
-
- // If this view contains the selected day
- protected boolean mHasSelectedDay = false;
-
- // Which day is selected [0-6] or -1 if no day is selected
- protected int mSelectedDay = -1;
-
- // How many days to display
- protected int mWeekDayCount;
-
- // The number of days + a spot for week number if it is displayed
- protected int mNumCells;
-
- // The left edge of the selected day
- protected int mSelectedLeft = -1;
-
- // The right edge of the selected day
- protected int mSelectedRight = -1;
-
- public WeekView(Context context) {
- super(context);
-
- TypedValue outTypedValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.dayPickerWeekViewStyle, outTypedValue, true);
- TypedArray attributesArray = context.obtainStyledAttributes(outTypedValue.resourceId,
- R.styleable.DayPickerWeekView);
- mHeight = attributesArray.getDimensionPixelSize(R.styleable.DayPickerWeekView_height,
- 26);
- mNumberTextSize = attributesArray.getDimensionPixelSize(
- R.styleable.DayPickerWeekView_textSize, 14);
- mFirstDayOfweek = attributesArray.getInt(R.styleable.DayPickerWeekView_weekStartDay,
- Calendar.SUNDAY);
- mNumCells = mWeekDayCount = attributesArray.getInt(
- R.styleable.DayPickerWeekView_weekDayCount, 7);
- mShowWeekNumber = attributesArray.getBoolean(R.styleable.DayPickerWeekView_weekDayCount,
- true);
- mWeekSeperatorWidth = attributesArray.getDimensionPixelSize(
- R.styleable.DayPickerWeekView_weekSeperatorWidth, 1);
- mWeekDayPadding = attributesArray.getDimensionPixelSize(
- R.styleable.DayPickerWeekView_weekDayPadding, 0);
- mSelectionBackgroundColor = attributesArray.getColor(
- R.styleable.DayPickerWeekView_selectionBackgroundColor, 0);
- mFocusedMonthDateColor = attributesArray.getColor(
- R.styleable.DayPickerWeekView_focusedMonthDateColor, 0);
- mOtherMonthDateColor = attributesArray.getColor(
- R.styleable.DayPickerWeekView_otherMonthDateColor, 0);
- mGridLinesColor = attributesArray.getColor(
- R.styleable.DayPickerWeekView_gridLinesColor, 0);
- mWeekNumberColor = attributesArray.getColor(
- R.styleable.DayPickerWeekView_weekNumberColor, 0);
- mSelectedDayLine = attributesArray
- .getDrawable(R.styleable.DayPickerWeekView_selectedDayLine);
- mSelectedDayLineWidth = attributesArray.getDimensionPixelSize(
- R.styleable.DayPickerWeekView_selectedDayLineWidth, 6);
- attributesArray.recycle();
-
- // Sets up any standard paints that will be used
- setPaintProperties();
- }
-
- /**
- * Sets all the parameters for displaying this week. The only required
- * parameter is the week number. Other parameters have a default value
- * and will only update if a new value is included, except for focus
- * month, which will always default to no focus month if no value is
- * passed in. See {@link #VIEW_PARAMS_HEIGHT} for more info on
- * parameters.
- *
- * @param params A map of the new parameters, see
- * {@link #VIEW_PARAMS_HEIGHT}
- */
- public void setWeekParams(HashMap<String, Object> params) {
- if (!params.containsKey(VIEW_PARAMS_WEEK)) {
- throw new InvalidParameterException(
- "You must specify the week number for this view");
- }
- setTag(params);
- // We keep the current value for any params not present
- if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
- mHeight = ((int[]) params.get(VIEW_PARAMS_HEIGHT))[0];
- }
- if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) {
- mSelectedDay = ((int[]) params.get(VIEW_PARAMS_SELECTED_DAY))[0];
- }
- mHasSelectedDay = mSelectedDay != -1;
- if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) {
- mWeekDayCount = ((int[]) params.get(VIEW_PARAMS_NUM_DAYS))[0];
- }
- if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) {
- if (((int[]) params.get(VIEW_PARAMS_SHOW_WK_NUM))[0] != 0) {
- mNumCells = mWeekDayCount + 1;
- mShowWeekNumber = true;
- } else {
- mShowWeekNumber = false;
- }
- } else {
- mNumCells = mShowWeekNumber ? mWeekDayCount + 1 : mWeekDayCount;
- }
- mWeek = ((int[]) params.get(VIEW_PARAMS_WEEK))[0];
- mTempCalendar.setTimeInMillis(mRangeStartDate.getTimeInMillis());
- mTempCalendar.setTimeZone(mRangeStartDate.getTimeZone());
- mTempCalendar.add(Calendar.WEEK_OF_YEAR, mWeek);
- if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
- mTempCalendar.setFirstDayOfWeek(((int[]) params.get(VIEW_PARAMS_WEEK_START))[0]);
- } else {
- mTempCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
- }
-
- // Allocate space for caching the day numbers and focus values
- mDayNumbers = new String[mNumCells];
- mFocusDay = new boolean[mNumCells];
-
- // If we're showing the week number calculate it based on Monday
- int i = 0;
- if (mShowWeekNumber) {
- mDayNumbers[0] = Integer.toString(mTempCalendar.get(Calendar.WEEK_OF_YEAR));
- i++;
- }
-
- // Now adjust our starting day based on the start day of the week
- int diff = mTempCalendar.getFirstDayOfWeek() - mTempCalendar.get(Calendar.DAY_OF_WEEK);
- mTempCalendar.add(Calendar.DAY_OF_MONTH, diff);
-
- mFirstDay = (Calendar) mTempCalendar.clone();
-
- mMonthOfFirstWeekDay = mTempCalendar.get(Calendar.MONTH);
-
- int focusMonth = params.containsKey(VIEW_PARAMS_FOCUS_MONTH) ? ((int[]) params
- .get(VIEW_PARAMS_FOCUS_MONTH))[0] : -1;
-
- for (; i < mNumCells; i++) {
- mFocusDay[i] = (mTempCalendar.get(Calendar.MONTH) == focusMonth);
- // do not draw dates outside the valid range to avoid user confusion
- if (mTempCalendar.before(mRangeStartDate) || mTempCalendar.after(mRangeEndDate)) {
- mDayNumbers[i] = "";
- } else {
- mDayNumbers[i] = Integer.toString(mTempCalendar.get(Calendar.DAY_OF_MONTH));
- }
- mTempCalendar.add(Calendar.DAY_OF_MONTH, 1);
- }
- // We do one extra add at the end of the loop, if that pushed us to
- // new month undo it
- if (mTempCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
- mTempCalendar.add(Calendar.DAY_OF_MONTH, -1);
- }
- mLastWeekDayMonth = mTempCalendar.get(Calendar.MONTH);
-
- updateSelectionPositions();
- }
-
- /**
- * Sets up the text and style properties for painting. Override this if
- * you want to use a different paint.
- */
- protected void setPaintProperties() {
- mDrawPaint.setFakeBoldText(false);
- mDrawPaint.setAntiAlias(true);
- mDrawPaint.setTextSize(mNumberTextSize);
- mDrawPaint.setStyle(Style.FILL);
-
- mMonthNumDrawPaint.setFakeBoldText(true);
- mMonthNumDrawPaint.setAntiAlias(true);
- mMonthNumDrawPaint.setTextSize(mNumberTextSize);
- mMonthNumDrawPaint.setColor(mFocusedMonthDateColor);
- mMonthNumDrawPaint.setStyle(Style.FILL);
- mMonthNumDrawPaint.setTextAlign(Align.CENTER);
- }
-
- /**
- * Returns the month of the first day in this week.
- *
- * @return The month the first day of this view is in.
- */
- public int getMonthOfFirstWeekDay() {
- return mMonthOfFirstWeekDay;
- }
-
- /**
- * Returns the month of the last day in this week
- *
- * @return The month the last day of this view is in
- */
- public int getMonthOfLastWeekDay() {
- return mLastWeekDayMonth;
- }
-
- /**
- * Returns the first day in this view.
- *
- * @return The first day in the view.
- */
- public Calendar getFirstDay() {
- return mFirstDay;
- }
-
- /**
- * Returns the number of days this view will display.
- */
- public int getNumDays() {
- return mWeekDayCount;
- }
-
- /**
- * Calculates the day that the given x position is in, accounting for
- * week number. Returns a Time referencing that day or null if
- *
- * @param x The x position of the touch eventy
- */
- public void getDayFromLocation(float x, Calendar outCalendar) {
- int dayStart = mShowWeekNumber ? (mWidth - mWeekDayPadding * 2) / mNumCells
- + mWeekDayPadding : mWeekDayPadding;
- if (x < dayStart || x > mWidth - mWeekDayPadding) {
- outCalendar.set(0, 0, 0, 0, 0, 0);
- return;
- }
- // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
- int dayPosition = (int) ((x - dayStart) * mWeekDayCount
- / (mWidth - dayStart - mWeekDayPadding));
- outCalendar.setTimeZone(mFirstDay.getTimeZone());
- outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
- outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- drawBackground(canvas);
- drawWeekNums(canvas);
- drawDaySeparators(canvas);
- drawSelectedDayLines(canvas);
- }
-
- /**
- * This draws the selection highlight if a day is selected in this week.
- * Override this method if you wish to have a different background
- * drawn.
- *
- * @param canvas The canvas to draw on
- */
- protected void drawBackground(Canvas canvas) {
- if (!mHasSelectedDay) {
- return;
- }
- mDrawPaint.setColor(mSelectionBackgroundColor);
-
- mTempRect.top = mWeekSeperatorWidth;
- mTempRect.bottom = mHeight;
- mTempRect.left = mShowWeekNumber ? mWeekDayPadding + (mWidth - mWeekDayPadding * 2)
- / mNumCells : mWeekDayPadding;
- mTempRect.right = mSelectedLeft - 2;
- canvas.drawRect(mTempRect, mDrawPaint);
-
- mTempRect.left = mSelectedRight + 3;
- mTempRect.right = mWidth - mWeekDayPadding;
- canvas.drawRect(mTempRect, mDrawPaint);
- }
-
- /**
- * Draws the week and month day numbers for this week. Override this
- * method if you need different placement.
- *
- * @param canvas The canvas to draw on
- */
- protected void drawWeekNums(Canvas canvas) {
- float textHeight = mDrawPaint.getTextSize();
- int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorWidth;
- int nDays = mNumCells;
-
- mDrawPaint.setTextAlign(Align.CENTER);
- int i = 0;
- int divisor = 2 * nDays;
- if (mShowWeekNumber) {
- mDrawPaint.setColor(mWeekNumberColor);
- int x = (mWidth - mWeekDayPadding * 2) / divisor + mWeekDayPadding;
- canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
- i++;
- }
- for (; i < nDays; i++) {
- mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
- : mOtherMonthDateColor);
- int x = (2 * i + 1) * (mWidth - mWeekDayPadding * 2) / divisor + mWeekDayPadding;
- canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
- }
- }
-
- /**
- * Draws a horizontal line for separating the weeks. Override this
- * method if you want custom separators.
- *
- * @param canvas The canvas to draw on.
- */
- protected void drawDaySeparators(Canvas canvas) {
- // If it is the topmost fully visible child do not draw separator line
- int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
- if (mListView.getChildAt(0).getTop() < 0) {
- firstFullyVisiblePosition++;
- }
- if (firstFullyVisiblePosition == mWeek) {
- return;
- }
- mDrawPaint.setColor(mGridLinesColor);
- mDrawPaint.setStrokeWidth(mWeekSeperatorWidth);
- float x = mShowWeekNumber ? mWeekDayPadding + (mWidth - mWeekDayPadding * 2) / mNumCells
- : mWeekDayPadding;
- canvas.drawLine(x, 0, mWidth - mWeekDayPadding, 0, mDrawPaint);
- }
-
- /**
- * Draws the selected day lines if this week has a selected day.
- *
- * @param canvas The canvas to draw on
- */
- protected void drawSelectedDayLines(Canvas canvas) {
- if (!mHasSelectedDay) {
- return;
- }
- mSelectedDayLine.setBounds(mSelectedLeft - mSelectedDayLineWidth / 2,
- mWeekSeperatorWidth,
- mSelectedLeft + mSelectedDayLineWidth / 2, mHeight);
- mSelectedDayLine.draw(canvas);
- mSelectedDayLine.setBounds(mSelectedRight - mSelectedDayLineWidth / 2,
- mWeekSeperatorWidth,
- mSelectedRight + mSelectedDayLineWidth / 2, mHeight);
- mSelectedDayLine.draw(canvas);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
- updateSelectionPositions();
- }
-
- /**
- * This calculates the positions for the selected day lines.
- */
- protected void updateSelectionPositions() {
- if (mHasSelectedDay) {
- int selectedPosition = mSelectedDay - mTempCalendar.getFirstDayOfWeek();
- if (selectedPosition < 0) {
- selectedPosition += 7;
- }
- if (mShowWeekNumber) {
- selectedPosition++;
- }
- mSelectedLeft = selectedPosition * (mWidth - mWeekDayPadding * 2) / mNumCells
- + mWeekDayPadding;
- mSelectedRight = (selectedPosition + 1) * (mWidth - mWeekDayPadding * 2) / mNumCells
- + mWeekDayPadding;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
- }
- }
-}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index a236d27..7ad0390 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -30,8 +30,8 @@ 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.Paint.Align;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
@@ -41,11 +41,11 @@ import android.util.AttributeSet;
import android.util.SparseArray;
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.LayoutInflater.Filter;
import android.view.animation.OvershootInterpolator;
import android.view.inputmethod.InputMethodManager;
@@ -62,11 +62,18 @@ import android.view.inputmethod.InputMethodManager;
* <p>
* For an example of using this widget, see {@link android.widget.TimePicker}.
* </p>
+ *
+ * @attr ref android.R.styleable#NumberPicker_solidColor
*/
@Widget
public class NumberPicker extends LinearLayout {
/**
+ * 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 = 2;
@@ -115,6 +122,8 @@ public class NumberPicker extends LinearLayout {
* 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().
+ *
+ * @hide
*/
public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter() {
final StringBuilder mBuilder = new StringBuilder();
@@ -123,7 +132,7 @@ public class NumberPicker extends LinearLayout {
final Object[] mArgs = new Object[1];
- public String toString(int value) {
+ public String format(int value) {
mArgs[0] = value;
mBuilder.delete(0, mBuilder.length());
mFmt.format("%02d", mArgs);
@@ -159,22 +168,22 @@ public class NumberPicker extends LinearLayout {
/**
* Lower value of the range of numbers allowed for the NumberPicker
*/
- private int mStart;
+ private int mMinValue;
/**
* Upper value of the range of numbers allowed for the NumberPicker
*/
- private int mEnd;
+ private int mMaxValue;
/**
* Current value of this NumberPicker
*/
- private int mCurrent;
+ private int mValue;
/**
* Listener to be notified upon current value change.
*/
- private OnChangeListener mOnChangeListener;
+ private OnValueChangedListener mOnValueChangedListener;
/**
* Listener to be notified upon scroll state change.
@@ -189,7 +198,7 @@ public class NumberPicker extends LinearLayout {
/**
* The speed for updating the value form long press.
*/
- private long mLongPressUpdateInterval = 300;
+ private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
/**
* Cache for the string representation of selector indices.
@@ -308,7 +317,7 @@ public class NumberPicker extends LinearLayout {
/**
* Flag whether the selector should wrap around.
*/
- private boolean mWrapSelector;
+ private boolean mWrapSelectorWheel;
/**
* The back ground color used to optimize scroller fading.
@@ -326,9 +335,10 @@ public class NumberPicker extends LinearLayout {
private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
/**
- * The callback interface used to indicate the number value has changed.
+ * Interface to listen for changes of the current value.
*/
- public interface OnChangeListener {
+ public interface OnValueChangedListener {
+
/**
* Called upon a change of the current value.
*
@@ -336,11 +346,11 @@ public class NumberPicker extends LinearLayout {
* @param oldVal The previous value.
* @param newVal The new value.
*/
- void onChange(NumberPicker picker, int oldVal, int newVal);
+ void onValueChange(NumberPicker picker, int oldVal, int newVal);
}
/**
- * Interface for listening to the picker scroll state.
+ * Interface to listen for the picker scroll state.
*/
public interface OnScrollListener {
@@ -360,27 +370,29 @@ public class NumberPicker extends LinearLayout {
public static int SCROLL_STATE_FLING = 2;
/**
- * Callback method to be invoked while the number picker is being scrolled.
+ * 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}.
+ * @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 the number into a string for presentation.
+ * Interface used to format current value into a string for presentation.
*/
public interface Formatter {
/**
- * Formats a string representation of the current index.
+ * Formats a string representation of the current value.
*
* @param value The currently selected value.
* @return A formatted string representation.
*/
- public String toString(int value);
+ public String format(int value);
}
/**
@@ -436,9 +448,9 @@ public class NumberPicker extends LinearLayout {
public void onClick(View v) {
mInputText.clearFocus();
if (v.getId() == R.id.increment) {
- changeCurrent(mCurrent + 1);
+ changeCurrent(mValue + 1);
} else {
- changeCurrent(mCurrent - 1);
+ changeCurrent(mValue - 1);
}
}
};
@@ -551,6 +563,9 @@ public class NumberPicker extends LinearLayout {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mLastMotionEventY = mLastDownEventY = event.getY();
@@ -594,6 +609,9 @@ public class NumberPicker extends LinearLayout {
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!isEnabled()) {
+ return false;
+ }
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -702,12 +720,6 @@ public class NumberPicker extends LinearLayout {
}
}
- /**
- * Set the enabled state of this view. The interpretation of the enabled
- * state varies by subclass.
- *
- * @param enabled True if this view is enabled, false otherwise.
- */
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -716,9 +728,6 @@ public class NumberPicker extends LinearLayout {
mInputText.setEnabled(enabled);
}
- /**
- * Scrolls the selector with the given <code>vertical offset</code>.
- */
@Override
public void scrollBy(int x, int y) {
int[] selectorIndices = getSelectorIndices();
@@ -734,11 +743,11 @@ public class NumberPicker extends LinearLayout {
mSelectorElementHeight = mTextSize + selectorTextGapHeight;
}
- if (!mWrapSelector && y > 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mStart) {
+ if (!mWrapSelectorWheel && y > 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
mCurrentScrollOffset = mInitialScrollOffset;
return;
}
- if (!mWrapSelector && y < 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mEnd) {
+ if (!mWrapSelectorWheel && y < 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
mCurrentScrollOffset = mInitialScrollOffset;
return;
}
@@ -747,7 +756,7 @@ public class NumberPicker extends LinearLayout {
mCurrentScrollOffset -= mSelectorElementHeight;
decrementSelectorIndices(selectorIndices);
changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
- if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mStart) {
+ if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
mCurrentScrollOffset = mInitialScrollOffset;
}
}
@@ -755,7 +764,7 @@ public class NumberPicker extends LinearLayout {
mCurrentScrollOffset += mSelectorElementHeight;
incrementScrollSelectorIndices(selectorIndices);
changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
- if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mEnd) {
+ if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
mCurrentScrollOffset = mInitialScrollOffset;
}
}
@@ -769,16 +778,16 @@ public class NumberPicker extends LinearLayout {
/**
* Sets the listener to be notified on change of the current value.
*
- * @param onChangeListener The listener.
+ * @param onValueChangedListener The listener.
*/
- public void setOnChangeListener(OnChangeListener onChangeListener) {
- mOnChangeListener = onChangeListener;
+ public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
+ mOnValueChangedListener = onValueChangedListener;
}
/**
* Set listener to be notified for scroll state changes.
*
- * @param onScrollListener the callback, should not be null.
+ * @param onScrollListener The listener.
*/
public void setOnScrollListener(OnScrollListener onScrollListener) {
mOnScrollListener = onScrollListener;
@@ -787,165 +796,216 @@ public class NumberPicker extends LinearLayout {
/**
* Set the formatter to be used for formatting the current value.
* <p>
- * Note: If you have provided alternative values for the selected positons
- * this formatter is never invoked.
+ * Note: If you have provided alternative values for the values this
+ * formatter is never invoked.
* </p>
*
- * @param formatter the formatter object. If formatter is null,
- * String.valueOf() will be used.
+ * @param formatter The formatter object. If formatter is <code>null</code>,
+ * {@link String#valueOf(int)} will be used.
*
- * @see #setRange(int, int, String[])
+ * @see #setDisplayedValues(String[])
*/
public void setFormatter(Formatter formatter) {
+ if (formatter == mFormatter) {
+ return;
+ }
mFormatter = formatter;
- resetSelectorIndices();
+ resetSelectorWheelIndices();
}
/**
- * Set the range of numbers allowed for the number picker. The current value
- * will be automatically set to the start.
- *
- * @param start the start of the range (inclusive)
- * @param end the end of the range (inclusive)
- */
- public void setRange(int start, int end) {
- setRange(start, end, null);
- }
-
- /**
- * Set the range of numbers allowed for the number picker. The current value
- * will be automatically set to the start. Also provide a mapping for values
- * used to display to the user instead of the numbers in the range.
- *
- * @param start The start of the range (inclusive).
- * @param end The end of the range (inclusive).
- * @param displayedValues The values displayed to the user.
- */
- public void setRange(int start, int end, String[] displayedValues) {
- boolean wrapSelector = (end - start) >= mSelectorIndices.length;
- setRange(start, end, displayedValues, wrapSelector);
- }
-
- /**
- * Set the range of numbers allowed for the number picker. The current value
- * will be automatically set to the start. Also provide a mapping for values
- * used to display to the user.
+ * Set the current value for the number picker.
* <p>
- * Note: The <code>wrapSelectorWheel</code> argument is ignored if the range
- * (difference between <code>start</code> and <code>end</code>) us less than
- * five since this is the number of values shown by the selector wheel.
+ * If the argument is less than the {@link NumberPicker#getMinValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
+ * current value is set to the {@link NumberPicker#getMinValue()} value.
+ * </p>
+ * <p>
+ * If the argument is less than the {@link NumberPicker#getMinValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
+ * current value is set to the {@link NumberPicker#getMaxValue()} value.
+ * </p>
+ * <p>
+ * If the argument is less than the {@link NumberPicker#getMaxValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
+ * current value is set to the {@link NumberPicker#getMaxValue()} value.
+ * </p>
+ * <p>
+ * If the argument is less than the {@link NumberPicker#getMaxValue()} and
+ * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
+ * current value is set to the {@link NumberPicker#getMinValue()} value.
* </p>
*
- * @param start the start of the range (inclusive)
- * @param end the end of the range (inclusive)
- * @param displayedValues the values displayed to the user.
- * @param wrapSelectorWheel Whether to wrap the selector wheel.
- *
+ * @param value The current value.
* @see #setWrapSelectorWheel(boolean)
+ * @see #setMinValue(int)
+ * @see #setMaxValue(int)
*/
- public void setRange(int start, int end, String[] displayedValues, boolean wrapSelectorWheel) {
- if (start == mStart && end == mEnd) {
+ public void setValue(int value) {
+ if (mValue == value) {
return;
}
-
- if (start < 0 || end < 0) {
- throw new IllegalArgumentException("start and end must be > 0");
+ if (value < mMinValue) {
+ value = mWrapSelectorWheel ? mMaxValue : mMinValue;
}
-
- mDisplayedValues = displayedValues;
- mStart = start;
- mEnd = end;
- mCurrent = start;
-
- setWrapSelectorWheel(wrapSelectorWheel);
- updateInputTextView();
-
- if (displayedValues != 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);
+ if (value > mMaxValue) {
+ value = mWrapSelectorWheel ? mMinValue : mMaxValue;
}
-
- resetSelectorIndices();
+ mValue = value;
+ updateInputTextView();
+ updateIncrementAndDecrementButtonsVisibilityState();
}
/**
- * Set the current value for the number picker.
+ * Gets whether the selector wheel wraps when reaching the min/max value.
*
- * @param current the current value the start of the range (inclusive)
+ * @return True if the selector wheel wraps.
*
- * @throws IllegalArgumentException when current is not within the range of
- * of the number picker.
+ * @see #getMinValue()
+ * @see #getMaxValue()
*/
- public void setCurrent(int current) {
- if (mCurrent == current) {
- return;
- }
- if (current < mStart || current > mEnd) {
- throw new IllegalArgumentException("current should be >= start and <= end");
- }
- mCurrent = current;
- updateInputTextView();
- updateIncrementAndDecrementButtonsVisibilityState();
+ public boolean getWrapSelectorWheel() {
+ return mWrapSelectorWheel;
}
/**
- * Sets whether the selector wheel shown during flinging/scrolling should wrap
- * around the beginning and end values. By default if the range is more than
- * five (the number of items shown on the selector wheel) the selector wheel
- * wrapping is enabled.
+ * Sets whether the selector wheel shown during flinging/scrolling should
+ * wrap around the {@link NumberPicker#getMinValue()} and
+ * {@link NumberPicker#getMaxValue()} values.
+ * <p>
+ * By default if the range (max - min) is more than five (the number of
+ * items shown on the selector wheel) the selector wheel wrapping is
+ * enabled.
+ * </p>
*
* @param wrapSelector Whether to wrap.
*/
public void setWrapSelectorWheel(boolean wrapSelector) {
- if (wrapSelector && (mEnd - mStart) < mSelectorIndices.length) {
+ if (wrapSelector && (mMaxValue - mMinValue) < mSelectorIndices.length) {
throw new IllegalStateException("Range less than selector items count.");
}
- if (wrapSelector != mWrapSelector) {
+ if (wrapSelector != mWrapSelectorWheel) {
// force the selector indices array to be reinitialized
mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] = Integer.MAX_VALUE;
- mWrapSelector = wrapSelector;
+ mWrapSelectorWheel = wrapSelector;
}
}
/**
* Sets the speed at which the numbers be incremented and decremented when
* the up and down buttons are long pressed respectively.
+ * <p>
+ * The default value is 300 ms.
+ * </p>
*
* @param intervalMillis The speed (in milliseconds) at which the numbers
- * will be incremented and decremented (default 300ms).
+ * will be incremented and decremented.
*/
public void setOnLongPressUpdateInterval(long intervalMillis) {
mLongPressUpdateInterval = intervalMillis;
}
/**
- * Returns the current value of the NumberPicker.
+ * 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.
+ */
+ 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);
+ resetSelectorWheelIndices();
+ updateInputTextView();
+ updateIncrementAndDecrementButtonsVisibilityState();
+ }
+
+ /**
+ * Returns the max value of the picker.
*
- * @return the current value.
+ * @return The max value.
*/
- public int getCurrent() {
- return mCurrent;
+ public int getMaxValue() {
+ return mMaxValue;
+ }
+
+ /**
+ * Sets the max value of the picker.
+ *
+ * @param maxValue The max value.
+ */
+ 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);
+ resetSelectorWheelIndices();
+ updateInputTextView();
+ updateIncrementAndDecrementButtonsVisibilityState();
}
/**
- * Returns the range lower value of the NumberPicker.
+ * Gets the values to be displayed instead of string values.
*
- * @return The lower number of the range.
+ * @return The displayed values.
*/
- public int getRangeStart() {
- return mStart;
+ public String[] getDisplayedValues() {
+ return mDisplayedValues;
}
/**
- * Returns the range end value of the NumberPicker.
+ * Sets the values to be displayed.
*
- * @return The upper number of the range.
+ * @param displayedValues The displayed values.
*/
- public int getRangeEnd() {
- return mEnd;
+ 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();
+ resetSelectorWheelIndices();
}
@Override
@@ -1018,7 +1078,7 @@ public class NumberPicker extends LinearLayout {
* Resets the selector indices and clear the cached
* string representation of these indices.
*/
- private void resetSelectorIndices() {
+ private void resetSelectorWheelIndices() {
mSelectorIndexToStringCache.clear();
int[] selectorIdices = getSelectorIndices();
for (int i = 0; i < selectorIdices.length; i++) {
@@ -1035,15 +1095,15 @@ public class NumberPicker extends LinearLayout {
* @param current the new value of the NumberPicker
*/
private void changeCurrent(int current) {
- if (mCurrent == current) {
+ if (mValue == current) {
return;
}
// Wrap around the values if we go past the start or end
- if (mWrapSelector) {
+ if (mWrapSelectorWheel) {
current = getWrappedSelectorIndex(current);
}
- int previous = mCurrent;
- setCurrent(current);
+ int previous = mValue;
+ setValue(current);
notifyChange(previous, current);
}
@@ -1108,7 +1168,7 @@ public class NumberPicker extends LinearLayout {
mPreviousScrollerY = 0;
Scroller flingScroller = mFlingScroller;
- if (mWrapSelector) {
+ if (mWrapSelectorWheel) {
if (velocityY > 0) {
flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
} else {
@@ -1116,10 +1176,10 @@ public class NumberPicker extends LinearLayout {
}
} else {
if (velocityY > 0) {
- int maxY = mTextSize * (mCurrent - mStart);
+ int maxY = mTextSize * (mValue - mMinValue);
flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, maxY);
} else {
- int startY = mTextSize * (mEnd - mCurrent);
+ int startY = mTextSize * (mMaxValue - mValue);
int maxY = startY;
flingScroller.fling(0, startY, 0, velocityY, 0, 0, 0, maxY);
}
@@ -1153,12 +1213,12 @@ public class NumberPicker extends LinearLayout {
* Updates the visibility state of the increment and decrement buttons.
*/
private void updateIncrementAndDecrementButtonsVisibilityState() {
- if (mWrapSelector || mCurrent < mEnd) {
+ if (mWrapSelectorWheel || mValue < mMaxValue) {
mIncrementButton.setVisibility(VISIBLE);
} else {
mIncrementButton.setVisibility(INVISIBLE);
}
- if (mWrapSelector || mCurrent > mStart) {
+ if (mWrapSelectorWheel || mValue > mMinValue) {
mDecrementButton.setVisibility(VISIBLE);
} else {
mDecrementButton.setVisibility(INVISIBLE);
@@ -1170,11 +1230,11 @@ public class NumberPicker extends LinearLayout {
* the middle one.
*/
private int[] getSelectorIndices() {
- int current = getCurrent();
+ int current = getValue();
if (mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] != current) {
for (int i = 0; i < mSelectorIndices.length; i++) {
int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
- if (mWrapSelector) {
+ if (mWrapSelectorWheel) {
selectorIndex = getWrappedSelectorIndex(selectorIndex);
}
mSelectorIndices[i] = selectorIndex;
@@ -1188,10 +1248,10 @@ public class NumberPicker extends LinearLayout {
* @return The wrapped index <code>selectorIndex</code> value.
*/
private int getWrappedSelectorIndex(int selectorIndex) {
- if (selectorIndex > mEnd) {
- return mStart + (selectorIndex - mEnd) % (mEnd - mStart) - 1;
- } else if (selectorIndex < mStart) {
- return mEnd - (mStart - selectorIndex) % (mEnd - mStart) + 1;
+ if (selectorIndex > mMaxValue) {
+ return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
+ } else if (selectorIndex < mMinValue) {
+ return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
}
return selectorIndex;
}
@@ -1205,8 +1265,8 @@ public class NumberPicker extends LinearLayout {
selectorIndices[i] = selectorIndices[i + 1];
}
int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
- if (mWrapSelector && nextScrollSelectorIndex > mEnd) {
- nextScrollSelectorIndex = mStart;
+ if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
+ nextScrollSelectorIndex = mMinValue;
}
selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
@@ -1221,8 +1281,8 @@ public class NumberPicker extends LinearLayout {
selectorIndices[i] = selectorIndices[i - 1];
}
int nextScrollSelectorIndex = selectorIndices[1] - 1;
- if (mWrapSelector && nextScrollSelectorIndex < mStart) {
- nextScrollSelectorIndex = mEnd;
+ if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
+ nextScrollSelectorIndex = mMaxValue;
}
selectorIndices[0] = nextScrollSelectorIndex;
ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
@@ -1239,11 +1299,11 @@ public class NumberPicker extends LinearLayout {
if (scrollSelectorValue != null) {
return;
}
- if (selectorIndex < mStart || selectorIndex > mEnd) {
+ if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
scrollSelectorValue = "";
} else {
if (mDisplayedValues != null) {
- int displayedValueIndex = selectorIndex - mStart;
+ int displayedValueIndex = selectorIndex - mMinValue;
scrollSelectorValue = mDisplayedValues[displayedValueIndex];
} else {
scrollSelectorValue = formatNumber(selectorIndex);
@@ -1253,7 +1313,7 @@ public class NumberPicker extends LinearLayout {
}
private String formatNumber(int value) {
- return (mFormatter != null) ? mFormatter.toString(value) : String.valueOf(value);
+ return (mFormatter != null) ? mFormatter.format(value) : String.valueOf(value);
}
private void validateInputTextView(View v) {
@@ -1281,9 +1341,9 @@ public class NumberPicker extends LinearLayout {
* number.
*/
if (mDisplayedValues == null) {
- mInputText.setText(formatNumber(mCurrent));
+ mInputText.setText(formatNumber(mValue));
} else {
- mInputText.setText(mDisplayedValues[mCurrent - mStart]);
+ mInputText.setText(mDisplayedValues[mValue - mMinValue]);
}
mInputText.setSelection(mInputText.getText().length());
}
@@ -1293,8 +1353,8 @@ public class NumberPicker extends LinearLayout {
* NumberPicker.
*/
private void notifyChange(int previous, int current) {
- if (mOnChangeListener != null) {
- mOnChangeListener.onChange(this, previous, mCurrent);
+ if (mOnValueChangedListener != null) {
+ mOnValueChangedListener.onValueChange(this, previous, mValue);
}
}
@@ -1342,7 +1402,7 @@ public class NumberPicker extends LinearLayout {
// Don't force the user to type in jan when ja will do
value = value.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
- return mStart + i;
+ return mMinValue + i;
}
}
@@ -1357,7 +1417,7 @@ public class NumberPicker extends LinearLayout {
// Ignore as if it's not a number we don't care
}
}
- return mStart;
+ return mMinValue;
}
/**
@@ -1429,7 +1489,7 @@ public class NumberPicker extends LinearLayout {
* allowed. We have to allow less than min as the user might
* want to delete some numbers and then type a new number.
*/
- if (val > mEnd) {
+ if (val > mMaxValue) {
return "";
} else {
return filtered;
@@ -1493,7 +1553,7 @@ public class NumberPicker extends LinearLayout {
}
public void run() {
- changeCurrent(mCurrent + mUpdateStep);
+ changeCurrent(mValue + mUpdateStep);
postDelayed(this, mLongPressUpdateInterval);
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 203b637..107ea07 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -25,7 +25,7 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.NumberPicker.OnChangeListener;
+import android.widget.NumberPicker.OnValueChangedListener;
import java.text.DateFormatSymbols;
import java.util.Calendar;
@@ -68,13 +68,15 @@ public class TimePicker extends FrameLayout {
private boolean mIsAm;
// ui components
- private final NumberPicker mHourPicker;
- private final NumberPicker mMinutePicker;
- private final NumberPicker mAmPmPicker;
+ private final NumberPicker mHourSpinner;
+ private final NumberPicker mMinuteSpinner;
+ private final NumberPicker mAmPmSpinner;
private final TextView mDivider;
private final String[] mAmPmStrings;
+ private boolean mIsEnabled;
+
// callbacks
private OnTimeChangedListener mOnTimeChangedListener;
@@ -109,9 +111,9 @@ public class TimePicker extends FrameLayout {
true);
// hour
- mHourPicker = (NumberPicker) findViewById(R.id.hour);
- mHourPicker.setOnChangeListener(new NumberPicker.OnChangeListener() {
- public void onChange(NumberPicker spinner, int oldVal, int newVal) {
+ mHourSpinner = (NumberPicker) findViewById(R.id.hour);
+ mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangedListener() {
+ public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
mCurrentHour = newVal;
if (!mIs24HourView) {
// adjust from [1-12] to [0-11] internally, with the times
@@ -133,21 +135,22 @@ public class TimePicker extends FrameLayout {
mDivider.setText(R.string.time_picker_separator);
// digits of minute
- mMinutePicker = (NumberPicker) findViewById(R.id.minute);
- mMinutePicker.setRange(0, 59);
- mMinutePicker.setOnLongPressUpdateInterval(100);
- mMinutePicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
- mMinutePicker.setOnChangeListener(new NumberPicker.OnChangeListener() {
- public void onChange(NumberPicker spinner, int oldVal, int newVal) {
+ mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
+ mMinuteSpinner.setMinValue(0);
+ mMinuteSpinner.setMaxValue(59);
+ mMinuteSpinner.setOnLongPressUpdateInterval(100);
+ mMinuteSpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangedListener() {
+ public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
mCurrentMinute = newVal;
onTimeChanged();
}
});
// am/pm
- mAmPmPicker = (NumberPicker) findViewById(R.id.amPm);
- mAmPmPicker.setOnChangeListener(new OnChangeListener() {
- public void onChange(NumberPicker picker, int oldVal, int newVal) {
+ mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
+ mAmPmSpinner.setOnValueChangedListener(new OnValueChangedListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
picker.requestFocus();
if (mIsAm) {
// Currently AM switching to PM
@@ -187,10 +190,19 @@ public class TimePicker extends FrameLayout {
@Override
public void setEnabled(boolean enabled) {
+ if (mIsEnabled == enabled) {
+ return;
+ }
super.setEnabled(enabled);
- mMinutePicker.setEnabled(enabled);
- mHourPicker.setEnabled(enabled);
- mAmPmPicker.setEnabled(enabled);
+ mMinuteSpinner.setEnabled(enabled);
+ mHourSpinner.setEnabled(enabled);
+ mAmPmSpinner.setEnabled(enabled);
+ mIsEnabled = enabled;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mIsEnabled;
}
/**
@@ -228,6 +240,7 @@ public class TimePicker extends FrameLayout {
dest.writeInt(mMinute);
}
+ @SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR
= new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
@@ -313,7 +326,7 @@ public class TimePicker extends FrameLayout {
@Override
public int getBaseline() {
- return mHourPicker.getBaseline();
+ return mHourSpinner.getBaseline();
}
/**
@@ -329,22 +342,26 @@ public class TimePicker extends FrameLayout {
currentHour = 12;
}
}
- mHourPicker.setCurrent(currentHour);
+ mHourSpinner.setValue(currentHour);
mIsAm = mCurrentHour < 12;
- mAmPmPicker.setCurrent(mIsAm ? Calendar.AM : Calendar.PM);
+ mAmPmSpinner.setValue(mIsAm ? Calendar.AM : Calendar.PM);
onTimeChanged();
}
private void configurePickerRanges() {
if (mIs24HourView) {
- mHourPicker.setRange(0, 23);
- mHourPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
- mAmPmPicker.setVisibility(View.GONE);
+ mHourSpinner.setMinValue(0);
+ mHourSpinner.setMaxValue(23);
+ mHourSpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ mAmPmSpinner.setVisibility(View.GONE);
} else {
- mHourPicker.setRange(1, 12);
- mHourPicker.setFormatter(null);
- mAmPmPicker.setVisibility(View.VISIBLE);
- mAmPmPicker.setRange(0, 1, mAmPmStrings);
+ mHourSpinner.setMinValue(1);
+ mHourSpinner.setMaxValue(12);
+ mHourSpinner.setFormatter(null);
+ mAmPmSpinner.setVisibility(View.VISIBLE);
+ mAmPmSpinner.setMinValue(0);
+ mAmPmSpinner.setMaxValue(1);
+ mAmPmSpinner.setDisplayedValues(mAmPmStrings);
}
}
@@ -358,7 +375,7 @@ public class TimePicker extends FrameLayout {
* Set the state of the spinners appropriate to the current minute.
*/
private void updateMinuteDisplay() {
- mMinutePicker.setCurrent(mCurrentMinute);
+ mMinuteSpinner.setValue(mCurrentMinute);
onTimeChanged();
}
}
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 6f6b40b..e32c62d 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -82,6 +82,11 @@ public class LocalePicker extends ListFragment {
* {@link LocaleInfo#label}.
*/
public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
+ return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
+ }
+
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+ int layoutId, int fieldId) {
final Resources resources = context.getResources();
final String[] locales = context.getAssets().getLocales();
final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
@@ -149,8 +154,6 @@ public class LocalePicker extends ListFragment {
localeInfos[i] = preprocess[i];
}
Arrays.sort(localeInfos);
- final int layoutId = R.layout.locale_picker_item;
- final int fieldId = R.id.locale;
return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos);
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8eeed3d..e9566ad 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -123,6 +123,7 @@ LOCAL_SRC_FILES:= \
android_media_ToneGenerator.cpp \
android_hardware_Camera.cpp \
android_hardware_SensorManager.cpp \
+ android_hardware_UsbManager.cpp \
android_debug_JNITest.cpp \
android_util_FileObserver.cpp \
android/opengl/poly_clip.cpp.arm \
@@ -200,7 +201,8 @@ LOCAL_SHARED_LIBRARIES := \
libmedia \
libwpa_client \
libjpeg \
- libnfc_ndef
+ libnfc_ndef \
+ libusbhost
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2dfebe5..961bc1f1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,8 +77,8 @@ extern int register_android_opengl_jni_GLES11Ext(JNIEnv* env);
extern int register_android_opengl_jni_GLES20(JNIEnv* env);
extern int register_android_hardware_Camera(JNIEnv *env);
-
extern int register_android_hardware_SensorManager(JNIEnv *env);
+extern int register_android_hardware_UsbManager(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
@@ -1266,6 +1266,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_SensorManager),
+ REG_JNI(register_android_hardware_UsbManager),
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioTrack),
diff --git a/core/jni/android_hardware_UsbManager.cpp b/core/jni/android_hardware_UsbManager.cpp
new file mode 100644
index 0000000..8f32abf
--- /dev/null
+++ b/core/jni/android_hardware_UsbManager.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <usbhost/usbhost.h>
+
+#include <stdio.h>
+
+using namespace android;
+
+static jint android_hardware_UsbManager_get_device_id(JNIEnv *env, jobject clazz, jstring name)
+{
+ const char *nameStr = env->GetStringUTFChars(name, NULL);
+ int id = usb_device_get_unique_id_from_name(nameStr);
+ env->ReleaseStringUTFChars(name, nameStr);
+ return id;
+}
+
+static jstring android_hardware_UsbManager_get_device_name(JNIEnv *env, jobject clazz, jint id)
+{
+ char* name = usb_device_get_name_from_unique_id(id);
+ jstring result = env->NewStringUTF(name);
+ free(name);
+ return result;
+}
+
+static JNINativeMethod method_table[] = {
+ { "native_get_device_id", "(Ljava/lang/String;)I",
+ (void*)android_hardware_UsbManager_get_device_id },
+ { "native_get_device_name", "(I)Ljava/lang/String;",
+ (void*)android_hardware_UsbManager_get_device_name },
+};
+
+int register_android_hardware_UsbManager(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("android/hardware/UsbManager");
+ if (clazz == NULL) {
+ LOGE("Can't find android/hardware/UsbManager");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbManager",
+ method_table, NELEM(method_table));
+}
+
diff --git a/core/res/res/layout/day_picker.xml b/core/res/res/layout/calendar_view.xml
index a030df8..176bb8b 100644
--- a/core/res/res/layout/day_picker.xml
+++ b/core/res/res/layout/calendar_view.xml
@@ -48,43 +48,37 @@
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
+
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
<TextView android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:gravity="center"
- style="?android:attr/dayPickerWeekDayViewStyle" />
+ android:gravity="center" />
</LinearLayout>
diff --git a/core/res/res/layout/date_picker.xml b/core/res/res/layout/date_picker.xml
index 5c023ee..e9663b1 100644
--- a/core/res/res/layout/date_picker.xml
+++ b/core/res/res/layout/date_picker.xml
@@ -32,8 +32,8 @@
<LinearLayout android:id="@+id/pickers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="15dip"
- android:layout_weight="0.5"
+ android:layout_marginRight="22dip"
+ android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center">
@@ -42,10 +42,8 @@
android:id="@+id/month"
android:layout_width="48dip"
android:layout_height="wrap_content"
- android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
- android:layout_marginTop="35dip"
- android:layout_marginBottom="35dip"
+ android:layout_marginLeft="22dip"
+ android:layout_marginRight="22dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -55,10 +53,8 @@
android:id="@+id/day"
android:layout_width="48dip"
android:layout_height="wrap_content"
- android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
- android:layout_marginTop="35dip"
- android:layout_marginBottom="35dip"
+ android:layout_marginLeft="22dip"
+ android:layout_marginRight="22dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -68,25 +64,23 @@
android:id="@+id/year"
android:layout_width="48dip"
android:layout_height="wrap_content"
- android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
- android:layout_marginTop="35dip"
- android:layout_marginBottom="35dip"
+ android:layout_marginLeft="22dip"
+ android:layout_marginRight="22dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
</LinearLayout>
- <!-- mini-month day-picker -->
- <DayPicker
- android:id="@+id/mini_month_day_picker"
- android:layout_width="200dip"
- android:layout_height="230dip"
- android:layout_marginLeft="15dip"
- android:layout_weight="0.5"
- android:focusable="true"
- android:focusableInTouchMode="true"
- />
+ <!-- calendar view -->
+ <CalendarView
+ android:id="@+id/calendar_view"
+ android:layout_width="245dip"
+ android:layout_height="280dip"
+ android:layout_marginLeft="22dip"
+ android:layout_weight="1"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
</LinearLayout>
diff --git a/core/res/res/layout/date_picker_dialog.xml b/core/res/res/layout/date_picker_dialog.xml
index 148e192..004d52a 100644
--- a/core/res/res/layout/date_picker_dialog.xml
+++ b/core/res/res/layout/date_picker_dialog.xml
@@ -20,5 +20,6 @@
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/datePicker"
android:layout_gravity="center_horizontal"
- android:layout_width="250dip"
- android:layout_height="600dip"/>
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
diff --git a/core/res/res/layout/time_picker.xml b/core/res/res/layout/time_picker.xml
index bf81c18..382b2f6 100644
--- a/core/res/res/layout/time_picker.xml
+++ b/core/res/res/layout/time_picker.xml
@@ -30,9 +30,8 @@
android:id="@+id/hour"
android:layout_width="48dip"
android:layout_height="wrap_content"
- android:layout_marginRight="13dip"
- android:layout_marginTop="35dip"
- android:layout_marginBottom="35dip"
+ android:layout_marginLeft="22dip"
+ android:layout_marginRight="20dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -50,10 +49,8 @@
android:id="@+id/minute"
android:layout_width="48dip"
android:layout_height="wrap_content"
- android:layout_marginLeft="13dip"
- android:layout_marginRight="15dip"
- android:layout_marginTop="35dip"
- android:layout_marginBottom="35dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="22dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -63,9 +60,8 @@
android:id="@+id/amPm"
android:layout_width="48dip"
android:layout_height="wrap_content"
- android:layout_marginLeft="15dip"
- android:layout_marginTop="35dip"
- android:layout_marginBottom="35dip"
+ android:layout_marginLeft="22dip"
+ android:layout_marginRight="22dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 12ff047..f6899ad 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -504,21 +504,17 @@
<!-- Default PopupMenu style. -->
<attr name="popupMenuStyle" format="reference" />
- <!-- @hide NumberPicker up button style -->
+ <!-- NumberPicker style. -->
+ <attr name="numberPickerStyle" format="reference" />
+ <!-- NumberPicker up button style. -->
<attr name="numberPickerUpButtonStyle" format="reference" />
- <!-- @hide NumberPicker down button style -->
+ <!-- NumberPicker down button style. -->
<attr name="numberPickerDownButtonStyle" format="reference" />
- <!-- @hide NumberPicker input text style -->
+ <!-- NumberPicker input text style. -->
<attr name="numberPickerInputTextStyle" format="reference" />
- <!-- @hide NumberPicker the fading edge length of the selector wheel -->
- <attr name="numberPickerStyle" format="reference" />
-
- <!-- @hide DayPicker$WeekView style-->
- <attr name="dayPickerWeekViewStyle" format="reference" />
-
- <!-- @hide DayPickerDayView style-->
- <attr name="dayPickerWeekDayViewStyle" format="reference" />
+ <!-- The CalndarView style. -->
+ <attr name="calendarViewStyle" format="reference" />
<!-- Fast scroller styles -->
<eat-comment />
@@ -2826,6 +2822,14 @@
<attr name="startYear" format="integer" />
<!-- The last year (inclusive), for example "2010". -->
<attr name="endYear" format="integer" />
+ <!-- Whether the spinners are shown. -->
+ <attr name="spinnersShown" format="boolean" />
+ <!-- Whether the calendar view is shown. -->
+ <attr name="calendarViewShown" format="boolean" />
+ <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
+ <attr name="minDate" format="string" />
+ <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
+ <attr name="maxDate" format="string" />
</declare-styleable>
<declare-styleable name="TwoLineListItem">
@@ -2994,27 +2998,38 @@
<attr name="minorWeightMax" format="float" />
</declare-styleable>
- <!-- @hide -->
- <declare-styleable name="DayPickerWeekView">
- <attr name="height" />
- <attr name="weekStartDay" format="integer|reference" />
- <attr name="weekDayCount" format="integer|reference" />
- <attr name="showWeekNumber" format="boolean|reference" />
- <attr name="weekSeperatorWidth" format="dimension" />
- <attr name="textSize" />
- <attr name="weekDayPadding" format="dimension" />
- <attr name="selectedDayLineWidth" format="dimension" />
- <attr name="selectionBackgroundColor" format="color|reference" />
+ <declare-styleable name="CalendarView">
+ <!-- The first day of week according to {@link java.util.Calendar}. -->
+ <attr name="firstDayOfWeek" format="integer" />
+ <!-- Whether do show week numbers. -->
+ <attr name="showWeekNumber" format="boolean" />
+ <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
+ <attr name="minDate" />
+ <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
+ <attr name="maxDate" />
+ <!-- The number of weeks to be shown. -->
+ <attr name="shownWeekCount" format="integer"/>
+ <!-- The background color for the selected week. -->
+ <attr name="selectedWeekBackgroundColor" format="color|reference" />
+ <!-- The color for the dates of the selected month. -->
<attr name="focusedMonthDateColor" format="color|reference" />
- <attr name="otherMonthDateColor" format="color|reference" />
+ <!-- The color for the dates of an unfocused month. -->
+ <attr name="unfocusedMonthDateColor" format="color|reference" />
+ <!-- The color for the week numbers. -->
<attr name="weekNumberColor" format="color|reference" />
- <attr name="gridLinesColor" format="color|reference" />
- <attr name="selectedDayLine" format="reference" />
+ <!-- The color for the sepatator line between weeks. -->
+ <attr name="weekSeparatorLineColor" format="color|reference" />
+ <!-- Drawable for the vertical bar shown at the beggining and at the end of a selected date. -->
+ <attr name="selectedDateVerticalBar" format="reference" />
+ <!-- The text appearance for the week day abbreviation of the calendar header. -->
+ <attr name="weekDayTextAppearance" format="reference" />
+ <!-- The text appearance for the calendar dates. -->
+ <attr name="dateTextAppearance" format="reference" />
</declare-styleable>
- <!-- @hide -->
<declare-styleable name="NumberPicker">
<attr name="orientation" />
+ <!-- Color for the solid color background if such for optimized rendering. -->
<attr name="solidColor" format="color|reference" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8edea0d..590baf1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -497,6 +497,9 @@
<!-- Enables SIP on WIFI only -->
<bool name="config_sip_wifi_only">true</bool>
+ <!-- Enables built-in SIP phone capability -->
+ <bool name="config_built_in_sip_phone">false</bool>
+
<!-- Boolean indicating if restoring network selection should be skipped -->
<!-- The restoring is handled by modem if it is true-->
<bool translatable="false" name="skip_restoring_network_selection">false</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5aed668..9f1b9a1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1409,6 +1409,22 @@
<public type="attr" name="fastScrollOverlayPosition" />
<public type="attr" name="customTokens" />
<public type="attr" name="nextFocusForward" />
+ <public type="attr" name="firstDayOfWeek" />
+ <public type="attr" name="showWeekNumber" />
+ <public type="attr" name="minDate" />
+ <public type="attr" name="maxDate" />
+ <public type="attr" name="shownWeekCount" />
+ <public type="attr" name="selectedWeekBackgroundColor" />
+ <public type="attr" name="focusedMonthDateColor" />
+ <public type="attr" name="unfocusedMonthDateColor" />
+ <public type="attr" name="weekNumberColor" />
+ <public type="attr" name="weekSeparatorLineColor" />
+ <public type="attr" name="selectedDateVerticalBar" />
+ <public type="attr" name="weekDayTextAppearance" />
+ <public type="attr" name="dateTextAppearance" />
+ <public type="attr" name="solidColor" />
+ <public type="attr" name="spinnersShown" />
+ <public type="attr" name="calendarViewShown" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
@@ -1578,5 +1594,18 @@
<public type="style" name="Holo.Light.ButtonBar.AlertDialog" />
<public type="style" name="Holo.SegmentedButton" />
<public type="style" name="Holo.Light.SegmentedButton" />
+ <public type="style" name="Widget.ImageButton.NumberPickerUpButton" />
+ <public type="style" name="Widget.EditText.NumberPickerInputText" />
+ <public type="style" name="Widget.ImageButton.NumberPickerDownButton" />
+ <public type="style" name="Widget.Holo.ImageButton.NumberPickerUpButton" />
+ <public type="style" name="Widget.Holo.EditText.NumberPickerInputText" />
+ <public type="style" name="Widget.Holo.ImageButton.NumberPickerDownButton" />
+ <public type="style" name="Widget.Holo.Light.ImageButton.NumberPickerUpButton" />
+ <public type="style" name="Widget.Holo.Light.EditText.NumberPickerInputText" />
+ <public type="style" name="Widget.Holo.Light.ImageButton.NumberPickerDownButton" />
+ <public type="style" name="Widget.CalendarView" />
+ <public type="style" name="Widget.Holo.CalendarView" />
+ <public type="style" name="Widget.Holo.Light.CalendarView" />
<public type="string" name="selectTextMode" />
+
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9c28922..f6c88e7 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -238,8 +238,7 @@
<item name="android:textColor">#ff272727</item>
</style>
- <!-- @hide -->
- <style name="TextAppearance.Small.DayPickerWeekDayView">
+ <style name="TextAppearance.Small.CalendarViewWeekDayView">
<item name="android:textStyle">bold</item>
</style>
@@ -473,25 +472,22 @@
<item name="android:background">@android:drawable/btn_default</item>
</style>
- <!-- @hide -->
- <style name="Widget.DayPickerWeekView">
- <item name="android:height">26dip</item>
- <item name="android:weekStartDay">1</item>
- <item name="android:weekDayCount">7</item>
+ <style name="Widget.CalendarView">
<item name="android:showWeekNumber">true</item>
- <item name="android:weekSeperatorWidth">1dip</item>
- <item name="android:textSize">14dip</item>
- <item name="android:weekDayPadding">0dip</item>
- <item name="android:selectedDayLineWidth">6dip</item>
- <item name="android:selectionBackgroundColor">#330099FF</item>
+ <item name="android:firstDayOfWeek">1</item>
+ <item name="android:minDate">01/01/1900</item>
+ <item name="android:maxDate">12/31/2100</item>
+ <item name="android:shownWeekCount">6</item>
+ <item name="android:selectedWeekBackgroundColor">#330099FF</item>
<item name="android:focusedMonthDateColor">#FFFFFFFF</item>
- <item name="android:otherMonthDateColor">#66FFFFFF</item>
+ <item name="android:unfocusedMonthDateColor">#66FFFFFF</item>
<item name="android:weekNumberColor">#33FFFFFF</item>
- <item name="android:gridLinesColor">#19FFFFFF</item>
- <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo</item>
+ <item name="android:weekSeparatorLineColor">#19FFFFFF</item>
+ <item name="android:selectedDateVerticalBar">@android:drawable/day_picker_week_view_dayline_holo</item>
+ <item name="android:weekDayTextAppearance">@android:style/TextAppearance.Small.CalendarViewWeekDayView</item>
+ <item name="android:dateTextAppearance">?android:attr/textAppearanceSmall</item>
</style>
- <!-- @hide -->
<style name="Widget.NumberPicker">
<item name="android:orientation">vertical</item>
<item name="android:fadingEdge">vertical</item>
@@ -499,17 +495,14 @@
<item name="android:solidColor">@android:color/transparent</item>
</style>
- <!-- @hide -->
<style name="Widget.ImageButton.NumberPickerUpButton">
<item name="android:background">@android:drawable/timepicker_up_btn</item>
</style>
- <!-- @hide -->
<style name="Widget.ImageButton.NumberPickerDownButton">
<item name="android:background">@android:drawable/timepicker_down_btn</item>
</style>
- <!-- @hide -->
<style name="Widget.EditText.NumberPickerInputText">
<item name="android:textAppearance">@style/TextAppearance.Large.Inverse.NumberPickerInputText</item>
<item name="android:gravity">center</item>
@@ -861,7 +854,6 @@
<item name="android:textStyle">bold</item>
</style>
- <!-- @hide -->
<style name="TextAppearance.Large.Inverse.NumberPickerInputText">
<item name="android:textColor">@android:color/primary_text_light</item>
<item name="android:textSize">30sp</item>
@@ -1242,8 +1234,7 @@
<item name="android:textSize">18sp</item>
</style>
- <!-- @hide -->
- <style name="TextAppearance.Holo.DayPickerWeekDayView" parent="TextAppearance.Small.DayPickerWeekDayView">
+ <style name="TextAppearance.Holo.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView">
<item name="android:textColor">#505050</item>
</style>
@@ -1343,8 +1334,7 @@
<item name="android:textSize">18sp</item>
</style>
- <!-- @hide -->
- <style name="TextAppearance.Holo.Light.DayPickerWeekDayView" parent="TextAppearance.Small.DayPickerWeekDayView">
+ <style name="TextAppearance.Holo.Light.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView">
</style>
<!-- Widget Styles -->
@@ -1457,35 +1447,34 @@
<item name="android:listSelector">?android:attr/selectableItemBackground</item>
</style>
- <!-- @hide -->
- <style name="Widget.Holo.DayPickerWeekView" parent="Widget.DayPickerWeekView">
- <item name="android:selectionBackgroundColor">#330099FF</item>
+ <style name="Widget.Holo.CalendarView" parent="Widget.CalendarView">
+ <item name="android:selectedWeekBackgroundColor">#330099FF</item>
<item name="android:focusedMonthDateColor">#FFFFFFFF</item>
- <item name="android:otherMonthDateColor">#66FFFFFF</item>
+ <item name="android:unfocusedMonthDateColor">#66FFFFFF</item>
<item name="android:weekNumberColor">#33FFFFFF</item>
- <item name="android:gridLinesColor">#19FFFFFF</item>
- <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo</item>
+ <item name="android:weekSeparatorLineColor">#19FFFFFF</item>
+ <item name="android:selectedDateVerticalBar">@android:drawable/day_picker_week_view_dayline_holo</item>
+ <item name="android:weekDayTextAppearance">@android:style/TextAppearance.Holo.CalendarViewWeekDayView</item>
</style>
<style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
<item name="android:background">@android:drawable/btn_default_holo_dark</item>
</style>
- <!-- @hide -->
<style name="Widget.Holo.ImageButton.NumberPickerUpButton">
<item name="android:background">@null</item>
- <item name="android:paddingBottom">26sp</item>
<item name="android:src">@android:drawable/timepicker_up_btn_holo_dark</item>
+ <item name="android:paddingTop">26dip</item>
+ <item name="android:paddingBottom">26dip</item>
</style>
- <!-- @hide -->
<style name="Widget.Holo.ImageButton.NumberPickerDownButton">
<item name="android:background">@null</item>
- <item name="android:paddingTop">26sp</item>
<item name="android:src">@android:drawable/timepicker_down_btn_holo_dark</item>
+ <item name="android:paddingTop">26dip</item>
+ <item name="android:paddingBottom">26dip</item>
</style>
- <!-- @hide -->
<style name="Widget.Holo.EditText.NumberPickerInputText">
<item name="android:paddingTop">13sp</item>
<item name="android:paddingBottom">13sp</item>
@@ -1831,27 +1820,23 @@
<item name="android:background">@android:drawable/btn_default_holo_light</item>
</style>
- <!-- @hide -->
- <style name="Widget.Holo.Light.DayPickerWeekView" parent="Widget.DayPickerWeekView">
- <item name="android:selectionBackgroundColor">#330066ff</item>
+ <style name="Widget.Holo.Light.CalendarView" parent="Widget.CalendarView">
+ <item name="android:selectedWeekBackgroundColor">#330066ff</item>
<item name="android:focusedMonthDateColor">#FF000000</item>
- <item name="android:otherMonthDateColor">#7F08002B</item>
+ <item name="android:unfocusedMonthDateColor">#7F08002B</item>
<item name="android:weekNumberColor">#7F080021</item>
- <item name="android:gridLinesColor">#7F08002A</item>
- <item name="selectedDayLine">@android:drawable/day_picker_week_view_dayline_holo</item>
+ <item name="android:weekSeparatorLineColor">#7F08002A</item>
+ <item name="android:weekDayTextAppearance">@android:style/TextAppearance.Holo.Light.CalendarViewWeekDayView</item>
</style>
- <!-- @hide -->
<style name="Widget.Holo.Light.ImageButton.NumberPickerUpButton" parent="Widget.Holo.ImageButton.NumberPickerUpButton">
<item name="android:src">@android:drawable/timepicker_up_btn_holo_light</item>
</style>
- <!-- @hide -->
<style name="Widget.Holo.Light.ImageButton.NumberPickerDownButton" parent="Widget.Holo.ImageButton.NumberPickerDownButton">
<item name="android:src">@android:drawable/timepicker_down_btn_holo_light</item>
</style>
- <!-- @hide -->
<style name="Widget.Holo.Light.EditText.NumberPickerInputText" parent="Widget.Holo.EditText.NumberPickerInputText">
</style>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 1bbe22e..43686bc 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -273,7 +273,6 @@
<item name="searchViewEditQuery">@android:drawable/ic_commit</item>
<item name="searchViewEditQueryBackground">?attr/selectableItemBackground</item>
-
<!-- PreferenceFrameLayout attributes -->
<item name="preferenceFrameLayoutStyle">@android:style/Widget.PreferenceFrameLayout</item>
@@ -283,11 +282,8 @@
<item name="numberPickerInputTextStyle">@style/Widget.EditText.NumberPickerInputText</item>
<item name="numberPickerStyle">@style/Widget.NumberPicker</item>
- <!-- DayPicker$WeekView style-->
- <item name="dayPickerWeekViewStyle">@style/Widget.DayPickerWeekView</item>
-
- <!-- DayPickerWeekDayView style-->
- <item name="dayPickerWeekDayViewStyle">@style/TextAppearance.Small.DayPickerWeekDayView</item>
+ <!-- CalendarView style-->
+ <item name="calendarViewStyle">@style/Widget.CalendarView</item>
<item name="fastScrollThumbDrawable">@android:drawable/scrollbar_handle_accelerated_anim2</item>
<item name="fastScrollTrackDrawable">@null</item>
@@ -937,11 +933,8 @@
<item name="numberPickerDownButtonStyle">@style/Widget.Holo.ImageButton.NumberPickerDownButton</item>
<item name="numberPickerInputTextStyle">@style/Widget.Holo.EditText.NumberPickerInputText</item>
- <!-- DayPicker$WeekView style-->
- <item name="dayPickerWeekViewStyle">@style/Widget.Holo.DayPickerWeekView</item>
-
- <!-- DayPickerWeekDayView style-->
- <item name="dayPickerWeekDayViewStyle">@style/TextAppearance.Holo.DayPickerWeekDayView</item>
+ <!-- CalendarView style-->
+ <item name="calendarViewStyle">@style/Widget.Holo.CalendarView</item>
<item name="fastScrollThumbDrawable">@android:drawable/fastscroll_thumb_holo</item>
<item name="fastScrollPreviewBackgroundLeft">@android:drawable/fastscroll_label_left_holo_dark</item>
@@ -1194,11 +1187,8 @@
<item name="numberPickerDownButtonStyle">@style/Widget.Holo.Light.ImageButton.NumberPickerDownButton</item>
<item name="numberPickerInputTextStyle">@style/Widget.Holo.Light.EditText.NumberPickerInputText</item>
- <!-- DayPicker$WeekView style-->
- <item name="dayPickerWeekViewStyle">@style/Widget.Holo.Light.DayPickerWeekView</item>
-
- <!-- DayPickerWeekDayView style-->
- <item name="dayPickerWeekDayViewStyle">@style/TextAppearance.Holo.Light.DayPickerWeekDayView</item>
+ <!-- CalendarView style-->
+ <item name="calendarViewStyle">@style/Widget.Holo.Light.CalendarView</item>
<item name="fastScrollThumbDrawable">@android:drawable/fastscroll_thumb_holo</item>
<item name="fastScrollPreviewBackgroundLeft">@android:drawable/fastscroll_label_left_holo_light</item>