summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Blitzstein <sblitz@google.com>2013-04-11 16:44:38 -0700
committerSam Blitzstein <sblitz@google.com>2013-04-19 08:19:27 -0700
commitcb3f2522609186db6239ad154af275957118295c (patch)
treec33e8ca48f3e1483072843842f763e82593cd204
parent9884d4cda847b494c8174ec96b0014fa40032d95 (diff)
downloadandroid_frameworks_opt_datetimepicker-cb3f2522609186db6239ad154af275957118295c.tar.gz
android_frameworks_opt_datetimepicker-cb3f2522609186db6239ad154af275957118295c.tar.bz2
android_frameworks_opt_datetimepicker-cb3f2522609186db6239ad154af275957118295c.zip
Polishing accessibility for date picker.
Bug: 8531032 Change-Id: Idff339300a8edcd353b4d8e37e8b218b7b99460b
-rw-r--r--res/layout/date_picker_header_view.xml3
-rw-r--r--res/layout/date_picker_selected_date.xml6
-rw-r--r--res/layout/date_picker_view_animator.xml3
-rw-r--r--res/layout/time_header_label.xml7
-rw-r--r--src/com/android/datetimepicker/AccessibleLinearLayout.java46
-rw-r--r--src/com/android/datetimepicker/AccessibleTextView.java (renamed from src/com/android/datetimepicker/FakeButton.java)4
-rw-r--r--src/com/android/datetimepicker/Utils.java12
-rw-r--r--src/com/android/datetimepicker/date/AccessibleDateAnimator.java53
-rw-r--r--src/com/android/datetimepicker/date/DatePickerDialog.java56
-rw-r--r--src/com/android/datetimepicker/date/DayPickerView.java92
-rw-r--r--src/com/android/datetimepicker/date/SimpleMonthAdapter.java6
-rw-r--r--src/com/android/datetimepicker/date/SimpleMonthView.java35
-rw-r--r--src/com/android/datetimepicker/date/TextViewWithCircularIndicator.java16
-rw-r--r--src/com/android/datetimepicker/date/YearPickerView.java13
-rw-r--r--src/com/android/datetimepicker/time/TimePickerDialog.java26
15 files changed, 323 insertions, 55 deletions
diff --git a/res/layout/date_picker_header_view.xml b/res/layout/date_picker_header_view.xml
index 1314a1a..5fd73cc 100644
--- a/res/layout/date_picker_header_view.xml
+++ b/res/layout/date_picker_header_view.xml
@@ -22,4 +22,5 @@
android:gravity="center"
android:includeFontPadding="false"
android:textColor="@color/white"
- android:textSize="@dimen/date_picker_header_text_size" />
+ android:textSize="@dimen/date_picker_header_text_size"
+ android:importantForAccessibility="no" />
diff --git a/res/layout/date_picker_selected_date.xml b/res/layout/date_picker_selected_date.xml
index 22e5e92..2118ce1 100644
--- a/res/layout/date_picker_selected_date.xml
+++ b/res/layout/date_picker_selected_date.xml
@@ -24,7 +24,7 @@
android:gravity="center"
android:orientation="vertical" >
- <LinearLayout
+ <com.android.datetimepicker.AccessibleLinearLayout
android:id="@+id/date_picker_month_and_day"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -55,9 +55,9 @@
android:includeFontPadding="false"
android:textColor="@color/date_picker_selector"
android:textSize="@dimen/selected_date_day_size" />
- </LinearLayout>
+ </com.android.datetimepicker.AccessibleLinearLayout>
- <TextView
+ <com.android.datetimepicker.AccessibleTextView
android:id="@+id/date_picker_year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout/date_picker_view_animator.xml b/res/layout/date_picker_view_animator.xml
index 1b02070..04501bd 100644
--- a/res/layout/date_picker_view_animator.xml
+++ b/res/layout/date_picker_view_animator.xml
@@ -13,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ViewAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.datetimepicker.date.AccessibleDateAnimator
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/animator"
android:layout_width="@dimen/date_picker_component_width"
android:layout_height="@dimen/date_picker_view_animator_height"
diff --git a/res/layout/time_header_label.xml b/res/layout/time_header_label.xml
index 6062678..cd4a834 100644
--- a/res/layout/time_header_label.xml
+++ b/res/layout/time_header_label.xml
@@ -46,7 +46,7 @@
android:layout_marginLeft="@dimen/extra_time_label_margin"
android:layout_marginRight="@dimen/extra_time_label_margin"
android:layout_centerVertical="true" >
- <com.android.datetimepicker.FakeButton
+ <com.android.datetimepicker.AccessibleTextView
android:id="@+id/hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -87,7 +87,7 @@
android:layout_marginLeft="@dimen/extra_time_label_margin"
android:layout_marginRight="@dimen/extra_time_label_margin"
android:layout_centerVertical="true" >
- <com.android.datetimepicker.FakeButton
+ <com.android.datetimepicker.AccessibleTextView
android:id="@+id/minutes"
style="@style/time_label"
android:layout_width="wrap_content"
@@ -96,8 +96,7 @@
android:text="@string/time_placeholder"
android:layout_gravity="center" />
</FrameLayout>
-
- <com.android.datetimepicker.FakeButton
+ <com.android.datetimepicker.AccessibleTextView
android:id="@+id/ampm_hitspace"
android:layout_width="@dimen/ampm_label_size"
android:layout_height="wrap_content"
diff --git a/src/com/android/datetimepicker/AccessibleLinearLayout.java b/src/com/android/datetimepicker/AccessibleLinearLayout.java
new file mode 100644
index 0000000..629f856
--- /dev/null
+++ b/src/com/android/datetimepicker/AccessibleLinearLayout.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.datetimepicker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+/**
+ * Fake Button class, used so TextViews can announce themselves as Buttons, for accessibility.
+ */
+public class AccessibleLinearLayout extends LinearLayout {
+
+ public AccessibleLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(Button.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Button.class.getName());
+ }
+}
diff --git a/src/com/android/datetimepicker/FakeButton.java b/src/com/android/datetimepicker/AccessibleTextView.java
index 6ab1b61..98fa744 100644
--- a/src/com/android/datetimepicker/FakeButton.java
+++ b/src/com/android/datetimepicker/AccessibleTextView.java
@@ -26,9 +26,9 @@ import android.widget.TextView;
/**
* Fake Button class, used so TextViews can announce themselves as Buttons, for accessibility.
*/
-public class FakeButton extends TextView {
+public class AccessibleTextView extends TextView {
- public FakeButton(Context context, AttributeSet attrs) {
+ public AccessibleTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
diff --git a/src/com/android/datetimepicker/Utils.java b/src/com/android/datetimepicker/Utils.java
index 83590bf..ac603bc 100644
--- a/src/com/android/datetimepicker/Utils.java
+++ b/src/com/android/datetimepicker/Utils.java
@@ -19,6 +19,7 @@ package com.android.datetimepicker;
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.annotation.SuppressLint;
import android.os.Build;
import android.text.format.Time;
import android.view.View;
@@ -39,6 +40,17 @@ public class Utils {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
+ /**
+ * Try to speak the specified text, for accessibility. Only available on JB or later.
+ * @param text Text to announce.
+ */
+ @SuppressLint("NewApi")
+ public static void tryAccessibilityAnnounce(View view, CharSequence text) {
+ if (isJellybeanOrLater() && view != null && text != null) {
+ view.announceForAccessibility(text);
+ }
+ }
+
public static int getDaysInMonth(int month, int year) {
switch (month) {
case Calendar.JANUARY:
diff --git a/src/com/android/datetimepicker/date/AccessibleDateAnimator.java b/src/com/android/datetimepicker/date/AccessibleDateAnimator.java
new file mode 100644
index 0000000..fc022cd
--- /dev/null
+++ b/src/com/android/datetimepicker/date/AccessibleDateAnimator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.datetimepicker.date;
+
+import android.content.Context;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ViewAnimator;
+
+public class AccessibleDateAnimator extends ViewAnimator {
+ private long mDateMillis;
+
+ public AccessibleDateAnimator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setDateMillis(long dateMillis) {
+ mDateMillis = dateMillis;
+ }
+
+ /**
+ * Announce the currently-selected date when launched.
+ */
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ // Clear the event's current text so that only the current date will be spoken.
+ event.getText().clear();
+ int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
+ DateUtils.FORMAT_SHOW_WEEKDAY;
+
+ String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
+ event.getText().add(dateString);
+ return true;
+ }
+ return super.dispatchPopulateAccessibilityEvent(event);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/datetimepicker/date/DatePickerDialog.java b/src/com/android/datetimepicker/date/DatePickerDialog.java
index 8954c35..84ef2f3 100644
--- a/src/com/android/datetimepicker/date/DatePickerDialog.java
+++ b/src/com/android/datetimepicker/date/DatePickerDialog.java
@@ -20,9 +20,14 @@ import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.DialogFragment;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.Vibrator;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,6 +35,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.Button;
@@ -82,7 +88,7 @@ public class DatePickerDialog extends DialogFragment implements
private OnDateSetListener mCallBack;
private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
- private ViewAnimator mAnimator;
+ private AccessibleDateAnimator mAnimator;
private TextView mDayOfWeekView;
private LinearLayout mMonthAndDayView;
@@ -104,6 +110,12 @@ public class DatePickerDialog extends DialogFragment implements
private boolean mDelayAnimation = true;
+ // Accessibility strings.
+ private String mDayPickerDescription;
+ private String mSelectDay;
+ private String mYearPickerDescription;
+ private String mSelectYear;
+
/**
* The callback used to indicate the user is done filling in the date.
*/
@@ -219,9 +231,16 @@ public class DatePickerDialog extends DialogFragment implements
mDayPickerView = new DayPickerView(activity, this);
mYearPickerView = new YearPickerView(activity, this);
- mAnimator = (ViewAnimator) view.findViewById(R.id.animator);
+ Resources res = getResources();
+ mDayPickerDescription = res.getString(R.string.day_picker_description);
+ mSelectDay = res.getString(R.string.select_day);
+ mYearPickerDescription = res.getString(R.string.year_picker_description);
+ mSelectYear = res.getString(R.string.select_year);
+
+ mAnimator = (AccessibleDateAnimator) view.findViewById(R.id.animator);
mAnimator.addView(mDayPickerView);
mAnimator.addView(mYearPickerView);
+ mAnimator.setDateMillis(mCalendar.getTimeInMillis());
// TODO: Replace with animation decided upon by the design team.
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(ANIMATION_DURATION);
@@ -245,7 +264,7 @@ public class DatePickerDialog extends DialogFragment implements
}
});
- updateDisplay();
+ updateDisplay(false);
setCurrentView(currentView);
if (listPosition != -1) {
@@ -259,6 +278,8 @@ public class DatePickerDialog extends DialogFragment implements
}
private void setCurrentView(final int viewIndex) {
+ long millis = mCalendar.getTimeInMillis();
+
switch (viewIndex) {
case MONTH_AND_DAY_VIEW:
ObjectAnimator pulseAnimator = Utils.getPulseAnimator(mMonthAndDayView, 0.9f,
@@ -275,6 +296,11 @@ public class DatePickerDialog extends DialogFragment implements
mCurrentView = viewIndex;
}
pulseAnimator.start();
+
+ int flags = DateUtils.FORMAT_SHOW_DATE;
+ String dayString = DateUtils.formatDateTime(getActivity(), millis, flags);
+ mAnimator.setContentDescription(mDayPickerDescription+": "+dayString);
+ Utils.tryAccessibilityAnnounce(mAnimator, mSelectDay);
break;
case YEAR_VIEW:
pulseAnimator = Utils.getPulseAnimator(mYearView, 0.85f, 1.1f);
@@ -290,19 +316,37 @@ public class DatePickerDialog extends DialogFragment implements
mCurrentView = viewIndex;
}
pulseAnimator.start();
+
+ CharSequence yearString = YEAR_FORMAT.format(millis);
+ mAnimator.setContentDescription(mYearPickerDescription+": "+yearString);
+ Utils.tryAccessibilityAnnounce(mAnimator, mSelectYear);
break;
}
}
- private void updateDisplay() {
+ private void updateDisplay(boolean announce) {
if (mDayOfWeekView != null) {
mDayOfWeekView.setText(mCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
Locale.getDefault()).toUpperCase(Locale.getDefault()));
}
+
mSelectedMonthTextView.setText(mCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT,
Locale.getDefault()).toUpperCase(Locale.getDefault()));
mSelectedDayTextView.setText(DAY_FORMAT.format(mCalendar.getTime()));
mYearView.setText(YEAR_FORMAT.format(mCalendar.getTime()));
+
+ // Accessibility.
+ long millis = mCalendar.getTimeInMillis();
+ mAnimator.setDateMillis(millis);
+ int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
+ String monthAndDayText = DateUtils.formatDateTime(getActivity(), millis, flags);
+ mMonthAndDayView.setContentDescription(monthAndDayText);
+
+ if (announce) {
+ flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+ String fullDateText = DateUtils.formatDateTime(getActivity(), millis, flags);
+ Utils.tryAccessibilityAnnounce(mAnimator, fullDateText);
+ }
}
public void setFirstDayOfWeek(int startOfWeek) {
@@ -359,7 +403,7 @@ public class DatePickerDialog extends DialogFragment implements
mCalendar.set(Calendar.YEAR, year);
updatePickers();
setCurrentView(MONTH_AND_DAY_VIEW);
- updateDisplay();
+ updateDisplay(true);
}
@Override
@@ -368,7 +412,7 @@ public class DatePickerDialog extends DialogFragment implements
mCalendar.set(Calendar.MONTH, month);
mCalendar.set(Calendar.DAY_OF_MONTH, day);
updatePickers();
- updateDisplay();
+ updateDisplay(true);
}
private void updatePickers() {
diff --git a/src/com/android/datetimepicker/date/DayPickerView.java b/src/com/android/datetimepicker/date/DayPickerView.java
index fcd1d8c..5a49e42 100644
--- a/src/com/android/datetimepicker/date/DayPickerView.java
+++ b/src/com/android/datetimepicker/date/DayPickerView.java
@@ -16,18 +16,27 @@
package com.android.datetimepicker.date;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
+import com.android.datetimepicker.Utils;
import com.android.datetimepicker.date.DatePickerDialog.OnDateChangedListener;
import com.android.datetimepicker.date.SimpleMonthAdapter.CalendarDay;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
/**
* This displays a list of months in a calendar format with selectable days.
*/
@@ -50,6 +59,7 @@ public class DayPickerView extends ListView implements OnScrollListener, OnDateC
protected int mNumWeeks = 6;
protected boolean mShowWeekNumber = false;
protected int mDaysPerWeek = 7;
+ private static SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy", Locale.getDefault());
// These affect the scroll speed and feel
protected float mFriction = 1.0f;
@@ -78,6 +88,7 @@ public class DayPickerView extends ListView implements OnScrollListener, OnDateC
protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
private final DatePickerController mController;
+ private boolean mPerformingScroll;
public DayPickerView(Context context, DatePickerController controller) {
super(context);
@@ -162,7 +173,6 @@ public class DayPickerView extends ListView implements OnScrollListener, OnDateC
mTempDay.set(day);
final int position = (day.year - mController.getMinYear())
* SimpleMonthAdapter.MONTHS_IN_YEAR + day.month;
- Log.d(TAG, "Year: " + day.year);
View child;
int i = 0;
@@ -400,6 +410,84 @@ public class DayPickerView extends ListView implements OnScrollListener, OnDateC
protected void layoutChildren() {
final CalendarDay focusedDay = findAccessibilityFocus();
super.layoutChildren();
- restoreAccessibilityFocus(focusedDay);
+ if (mPerformingScroll) {
+ mPerformingScroll = false;
+ } else {
+ restoreAccessibilityFocus(focusedDay);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setItemCount(-1);
+ }
+
+ private String getMonthAndYearString(CalendarDay day) {
+ Calendar cal = Calendar.getInstance();
+ cal.set(day.year, day.month, day.day);
+
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append(cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
+ sbuf.append(" ");
+ sbuf.append(YEAR_FORMAT.format(cal.getTime()));
+ return sbuf.toString();
+ }
+
+ /**
+ * Necessary for accessibility, to ensure we support "scrolling" forward and backward
+ * in the month list.
+ */
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ }
+
+ /**
+ * When scroll forward/backward events are received, announce the newly scrolled-to month.
+ */
+ @SuppressLint("NewApi")
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
+ action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ // Figure out what month is showing.
+ int firstVisiblePosition = getFirstVisiblePosition();
+ int month = firstVisiblePosition % 12;
+ int year = firstVisiblePosition / 12 + mController.getMinYear();
+ CalendarDay day = new CalendarDay(year, month, 1);
+
+ // Scroll either forward or backward one month.
+ if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
+ day.month++;
+ if (day.month == 12) {
+ day.month = 0;
+ day.year++;
+ }
+ } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+ View firstVisibleView = getChildAt(0);
+ // If the view is fully visible, jump one month back. Otherwise, we'll just jump
+ // to the first day of first visible month.
+ if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
+ // There's an off-by-one somewhere, so the top of the first visible item will
+ // actually be -1 when it's at the exact top.
+ day.month--;
+ if (day.month == -1) {
+ day.month = 11;
+ day.year--;
+ }
+ }
+ }
+
+ // Go to that month.
+ Utils.tryAccessibilityAnnounce(this, getMonthAndYearString(day));
+ goTo(day, true, false, true);
+ mPerformingScroll = true;
+ return true;
}
}
diff --git a/src/com/android/datetimepicker/date/SimpleMonthAdapter.java b/src/com/android/datetimepicker/date/SimpleMonthAdapter.java
index 84be210..10a815a 100644
--- a/src/com/android/datetimepicker/date/SimpleMonthAdapter.java
+++ b/src/com/android/datetimepicker/date/SimpleMonthAdapter.java
@@ -16,6 +16,7 @@
package com.android.datetimepicker.date;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.view.View;
@@ -24,6 +25,7 @@ import android.widget.AbsListView.LayoutParams;
import android.widget.BaseAdapter;
import com.android.datetimepicker.date.SimpleMonthView.OnDayClickListener;
+import com.android.datetimepicker.R;
import java.util.Calendar;
import java.util.HashMap;
@@ -104,7 +106,7 @@ public class SimpleMonthAdapter extends BaseAdapter implements OnDayClickListene
/**
* Updates the selected day and related parameters.
*
- * @param selectedTime The time to highlight
+ * @param day The day to highlight
*/
public void setSelectedDay(CalendarDay day) {
mSelectedDay = day;
@@ -137,6 +139,7 @@ public class SimpleMonthAdapter extends BaseAdapter implements OnDayClickListene
return position;
}
+ @SuppressLint("NewApi")
@SuppressWarnings("unchecked")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
@@ -186,6 +189,7 @@ public class SimpleMonthAdapter extends BaseAdapter implements OnDayClickListene
return mSelectedDay.year == year && mSelectedDay.month == month;
}
+
@Override
public void onDayClick(SimpleMonthView view, CalendarDay day) {
if (day != null) {
diff --git a/src/com/android/datetimepicker/date/SimpleMonthView.java b/src/com/android/datetimepicker/date/SimpleMonthView.java
index bcb592f..832bdfb 100644
--- a/src/com/android/datetimepicker/date/SimpleMonthView.java
+++ b/src/com/android/datetimepicker/date/SimpleMonthView.java
@@ -27,6 +27,7 @@ import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.SparseArray;
@@ -52,6 +53,7 @@ import java.util.Locale;
* within the specified month.
*/
public class SimpleMonthView extends View {
+ private static final String TAG = "SimpleMonthView";
/**
* These params can be passed into the view to control how it appears.
@@ -404,17 +406,19 @@ public class SimpleMonthView extends View {
mNodeProvider.invalidateParent();
}
- private void drawMonthTitle(Canvas canvas) {
- int x = (mWidth + 2 * mPadding) / 2;
- int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + (MONTH_LABEL_TEXT_SIZE / 3);
+ private String getMonthAndYearString() {
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
| DateUtils.FORMAT_NO_MONTH_DAY;
-
mStringBuilder.setLength(0);
long millis = mCalendar.getTimeInMillis();
- String title = DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
+ return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
Time.getCurrentTimezone()).toString();
- canvas.drawText(title, x, y, mMonthTitlePaint);
+ }
+
+ private void drawMonthTitle(Canvas canvas) {
+ int x = (mWidth + 2 * mPadding) / 2;
+ int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + (MONTH_LABEL_TEXT_SIZE / 3);
+ canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint);
}
private void drawMonthDayLabels(Canvas canvas) {
@@ -455,7 +459,6 @@ public class SimpleMonthView extends View {
mMonthNumPaint.setColor(mDayTextColor);
}
canvas.drawText(String.format("%d", dayNumber), x, y, mMonthNumPaint);
-
j++;
if (j == mNumDays) {
j = 0;
@@ -550,6 +553,8 @@ public class SimpleMonthView extends View {
private final SparseArray<CalendarDay> mCachedItems = new SparseArray<CalendarDay>();
private final Rect mTempRect = new Rect();
+ Calendar recycle;
+
public MonthViewNodeProvider(Context context, View parent) {
super(context, parent);
}
@@ -659,19 +664,17 @@ public class SimpleMonthView extends View {
* @return A description of the time object
*/
private CharSequence getItemDescription(CalendarDay item) {
- final StringBuffer sbuf = new StringBuffer();
- sbuf.append(String.format("%d", item.day));
- sbuf.append(" ");
- sbuf.append(mCalendar.getDisplayName(Calendar.MONTH, Calendar.LONG,
- Locale.getDefault()));
- sbuf.append(" ");
- sbuf.append(String.format("%d", mYear));
+ if (recycle == null) {
+ recycle = Calendar.getInstance();
+ }
+ recycle.set(item.year, item.month, item.day);
+ CharSequence date = DateFormat.format("dd MMMM yyyy", recycle.getTimeInMillis());
if (item.day == mSelectedDay) {
- return getContext().getString(R.string.item_is_selected, sbuf);
+ return getContext().getString(R.string.item_is_selected, date);
}
- return sbuf;
+ return date;
}
}
diff --git a/src/com/android/datetimepicker/date/TextViewWithCircularIndicator.java b/src/com/android/datetimepicker/date/TextViewWithCircularIndicator.java
index 66140f5..09a0a4b 100644
--- a/src/com/android/datetimepicker/date/TextViewWithCircularIndicator.java
+++ b/src/com/android/datetimepicker/date/TextViewWithCircularIndicator.java
@@ -22,7 +22,9 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
+import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import com.android.datetimepicker.R;
@@ -38,6 +40,8 @@ public class TextViewWithCircularIndicator extends TextView {
private final int mRadius;
private final int mCircleColor;
+ private final String mItemIsSelectedText;
+
private boolean mDrawCircle;
public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
@@ -45,6 +49,8 @@ public class TextViewWithCircularIndicator extends TextView {
Resources res = context.getResources();
mCircleColor = res.getColor(R.color.blue);
mRadius = res.getDimensionPixelOffset(R.dimen.month_select_circle_radius);
+ mItemIsSelectedText = context.getResources().getString(R.string.item_is_selected);
+
init();
}
@@ -71,4 +77,14 @@ public class TextViewWithCircularIndicator extends TextView {
canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
}
}
+
+ @Override
+ public CharSequence getContentDescription() {
+ CharSequence itemText = getText();
+ if (mDrawCircle) {
+ return String.format(mItemIsSelectedText, itemText);
+ } else {
+ return itemText;
+ }
+ }
}
diff --git a/src/com/android/datetimepicker/date/YearPickerView.java b/src/com/android/datetimepicker/date/YearPickerView.java
index 411c574..70d2522 100644
--- a/src/com/android/datetimepicker/date/YearPickerView.java
+++ b/src/com/android/datetimepicker/date/YearPickerView.java
@@ -19,8 +19,10 @@ package com.android.datetimepicker.date;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.StateListDrawable;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
@@ -28,6 +30,7 @@ import android.widget.ListView;
import android.widget.TextView;
import com.android.datetimepicker.R;
+import com.android.datetimepicker.Utils;
import com.android.datetimepicker.date.DatePickerDialog.OnDateChangedListener;
import java.util.ArrayList;
@@ -37,6 +40,7 @@ import java.util.List;
* Displays a selectable list of years.
*/
public class YearPickerView extends ListView implements OnItemClickListener, OnDateChangedListener {
+ private static final String TAG = "YearPickerView";
private final DatePickerController mController;
private YearAdapter mAdapter;
@@ -147,4 +151,13 @@ public class YearPickerView extends ListView implements OnItemClickListener, OnD
mAdapter.notifyDataSetChanged();
postSetSelectionCentered(mController.getSelectedDay().year - mController.getMinYear());
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+ event.setFromIndex(0);
+ event.setToIndex(0);
+ }
+ }
}
diff --git a/src/com/android/datetimepicker/time/TimePickerDialog.java b/src/com/android/datetimepicker/time/TimePickerDialog.java
index 27742a2..2a385f7 100644
--- a/src/com/android/datetimepicker/time/TimePickerDialog.java
+++ b/src/com/android/datetimepicker/time/TimePickerDialog.java
@@ -17,7 +17,6 @@
package com.android.datetimepicker.time;
import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
import android.app.ActionBar.LayoutParams;
import android.app.DialogFragment;
import android.content.Context;
@@ -293,11 +292,11 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
private void updateAmPmDisplay(int amOrPm) {
if (amOrPm == AM) {
mAmPmTextView.setText(mAmText);
- tryAccessibilityAnnounce(mAmText);
+ Utils.tryAccessibilityAnnounce(mTimePicker, mAmText);
mAmPmHitspace.setContentDescription(mAmText);
} else if (amOrPm == PM){
mAmPmTextView.setText(mPmText);
- tryAccessibilityAnnounce(mPmText);
+ Utils.tryAccessibilityAnnounce(mTimePicker, mPmText);
mAmPmHitspace.setContentDescription(mPmText);
} else {
mAmPmTextView.setText(mDoublePlaceholderText);
@@ -330,7 +329,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
setCurrentItemShowing(MINUTE_INDEX, true, true, false);
announcement += ". " + mSelectMinutes;
}
- tryAccessibilityAnnounce(announcement);
+ Utils.tryAccessibilityAnnounce(mTimePicker, announcement);
} else if (pickerIndex == MINUTE_INDEX){
setMinute(newValue);
} else if (pickerIndex == AMPM_INDEX) {
@@ -359,7 +358,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
mHourView.setText(text);
mHourSpaceView.setText(text);
if (announce) {
- tryAccessibilityAnnounce(text);
+ Utils.tryAccessibilityAnnounce(mTimePicker, text);
}
}
@@ -368,7 +367,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
value = 0;
}
CharSequence text = String.format(Locale.getDefault(), "%02d", value);
- tryAccessibilityAnnounce(text);
+ Utils.tryAccessibilityAnnounce(mTimePicker, text);
mMinuteView.setText(text);
mMinuteSpaceView.setText(text);
}
@@ -386,14 +385,14 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
}
mTimePicker.setContentDescription(mHourPickerDescription+": "+hours);
if (announce) {
- tryAccessibilityAnnounce(mSelectHours);
+ Utils.tryAccessibilityAnnounce(mTimePicker, mSelectHours);
}
labelToAnimate = mHourView;
} else {
int minutes = mTimePicker.getMinutes();
mTimePicker.setContentDescription(mMinutePickerDescription+": "+minutes);
if (announce) {
- tryAccessibilityAnnounce(mSelectMinutes);
+ Utils.tryAccessibilityAnnounce(mTimePicker, mSelectMinutes);
}
labelToAnimate = mMinuteView;
}
@@ -411,17 +410,6 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
}
/**
- * Try to speak the specified text, for accessibility. Only available on JB or later.
- * @param text
- */
- @SuppressLint("NewApi")
- private void tryAccessibilityAnnounce(CharSequence text) {
- if (Utils.isJellybeanOrLater() && mTimePicker != null && text != null) {
- mTimePicker.announceForAccessibility(text);
- }
- }
-
- /**
* For keyboard mode, processes key events.
* @param keyCode the pressed key.
* @return true if the key was successfully processed, false otherwise.