Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
halo
rockchip_frameworks_opt_datetimepicker
Commits
2994491d
Commit
2994491d
authored
12 years ago
by
Alan Viverette
Browse files
Options
Download
Email Patches
Plain Diff
Switch calendar accessibility to use new ExploreByTouchHelper.
Change-Id: Idc2e268cdaa68524f7a5e7abf7276d8afec8dbba
parent
d461f9bf
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
73 additions
and
568 deletions
+73
-568
src/com/android/datetimepicker/date/SimpleMonthView.java
src/com/android/datetimepicker/date/SimpleMonthView.java
+73
-93
src/com/googlecode/eyesfree/utils/TouchExplorationHelper.java
...com/googlecode/eyesfree/utils/TouchExplorationHelper.java
+0
-475
No files found.
src/com/android/datetimepicker/date/SimpleMonthView.java
View file @
2994491d
...
...
@@ -27,10 +27,10 @@ import android.graphics.Typeface;
import
android.os.Bundle
;
import
android.support.v4.view.ViewCompat
;
import
android.support.v4.view.accessibility.AccessibilityNodeInfoCompat
;
import
android.support.v4.widget.ExploreByTouchHelper
;
import
android.text.format.DateFormat
;
import
android.text.format.DateUtils
;
import
android.text.format.Time
;
import
android.util.SparseArray
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.accessibility.AccessibilityEvent
;
...
...
@@ -39,7 +39,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import
com.android.datetimepicker.R
;
import
com.android.datetimepicker.Utils
;
import
com.android.datetimepicker.date.SimpleMonthAdapter.CalendarDay
;
import
com.googlecode.eyesfree.utils.TouchExplorationHelper
;
import
java.security.InvalidParameterException
;
import
java.util.Calendar
;
...
...
@@ -169,7 +168,7 @@ public class SimpleMonthView extends View {
private
final
Calendar
mCalendar
;
private
final
Calendar
mDayLabelCalendar
;
private
final
MonthView
NodeProvider
mNodeProvid
er
;
private
final
MonthView
TouchHelper
mTouchHelp
er
;
private
int
mNumRows
=
DEFAULT_NUM_ROWS
;
...
...
@@ -213,8 +212,8 @@ public class SimpleMonthView extends View {
-
MONTH_HEADER_SIZE
)
/
MAX_NUM_ROWS
;
// Set up accessibility components.
m
NodeProvid
er
=
new
MonthView
NodeProvider
(
context
,
this
);
ViewCompat
.
setAccessibilityDelegate
(
this
,
m
NodeProvider
.
getAccessibilityDelegate
()
);
m
TouchHelp
er
=
new
MonthView
TouchHelper
(
this
);
ViewCompat
.
setAccessibilityDelegate
(
this
,
m
TouchHelper
);
ViewCompat
.
setImportantForAccessibility
(
this
,
ViewCompat
.
IMPORTANT_FOR_ACCESSIBILITY_YES
);
mLockAccessibilityDelegate
=
true
;
...
...
@@ -236,20 +235,20 @@ public class SimpleMonthView extends View {
}
@Override
public
boolean
on
HoverEvent
(
MotionEvent
event
)
{
public
boolean
dispatch
HoverEvent
(
MotionEvent
event
)
{
// First right-of-refusal goes the touch exploration helper.
if
(
m
NodeProvider
.
onHover
(
this
,
event
))
{
if
(
m
TouchHelper
.
dispatchHoverEvent
(
event
))
{
return
true
;
}
return
super
.
on
HoverEvent
(
event
);
return
super
.
dispatch
HoverEvent
(
event
);
}
@Override
public
boolean
onTouchEvent
(
MotionEvent
event
)
{
switch
(
event
.
getAction
())
{
case
MotionEvent
.
ACTION_UP
:
final
CalendarDay
day
=
getDayFromLocation
(
event
.
getX
(),
event
.
getY
());
if
(
day
!
=
null
)
{
final
int
day
=
getDayFromLocation
(
event
.
getX
(),
event
.
getY
());
if
(
day
>
=
0
)
{
onDayClick
(
day
);
}
break
;
...
...
@@ -321,7 +320,6 @@ public class SimpleMonthView extends View {
*
* @param params A map of the new parameters, see
* {@link #VIEW_PARAMS_HEIGHT}
* @param tz The time zone this view should reference times in
*/
public
void
setMonthParams
(
HashMap
<
String
,
Integer
>
params
)
{
if
(!
params
.
containsKey
(
VIEW_PARAMS_MONTH
)
&&
!
params
.
containsKey
(
VIEW_PARAMS_YEAR
))
{
...
...
@@ -371,7 +369,7 @@ public class SimpleMonthView extends View {
mNumRows
=
calculateNumRows
();
// Invalidate cached accessibility information.
m
NodeProvid
er
.
invalidate
Paren
t
();
m
TouchHelp
er
.
invalidate
Roo
t
();
}
public
void
reuse
()
{
...
...
@@ -403,7 +401,7 @@ public class SimpleMonthView extends View {
mWidth
=
w
;
// Invalidate cached accessibility information.
m
NodeProvid
er
.
invalidate
Paren
t
();
m
TouchHelp
er
.
invalidate
Roo
t
();
}
private
String
getMonthAndYearString
()
{
...
...
@@ -475,16 +473,15 @@ public class SimpleMonthView extends View {
/**
* Calculates the day that the given x position is in, accounting for week
* number. Returns
a Time referencing that day or null if
* number. Returns
the day or -1 if the position wasn't in a day.
*
* @param x The x position of the touch event
* @return A time object for the tapped day or null if the position wasn't
* in a day
* @return The day number, or -1 if the position wasn't in a day
*/
public
CalendarDay
getDayFromLocation
(
float
x
,
float
y
)
{
public
int
getDayFromLocation
(
float
x
,
float
y
)
{
int
dayStart
=
mPadding
;
if
(
x
<
dayStart
||
x
>
mWidth
-
mPadding
)
{
return
null
;
return
-
1
;
}
// Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
int
row
=
(
int
)
(
y
-
MONTH_HEADER_SIZE
)
/
mRowHeight
;
...
...
@@ -493,24 +490,24 @@ public class SimpleMonthView extends View {
int
day
=
column
-
findDayOffset
()
+
1
;
day
+=
row
*
mNumDays
;
if
(
day
<
1
||
day
>
mNumCells
)
{
return
null
;
return
-
1
;
}
return
new
CalendarDay
(
mYear
,
mMonth
,
day
)
;
return
day
;
}
/**
* Called when the user clicks on a day. Handles callbacks to the
* {@link OnDayClickListener} if one is set.
*
* @param day
A time object representing t
he day that was clicked
* @param day
T
he day that was clicked
*/
private
void
onDayClick
(
CalendarDay
day
)
{
private
void
onDayClick
(
int
day
)
{
if
(
mOnDayClickListener
!=
null
)
{
mOnDayClickListener
.
onDayClick
(
this
,
day
);
mOnDayClickListener
.
onDayClick
(
this
,
new
CalendarDay
(
mYear
,
mMonth
,
day
)
)
;
}
// This is a no-op if accessibility is turned off.
m
NodeProvid
er
.
sendEventFor
Item
(
day
,
AccessibilityEvent
.
TYPE_VIEW_CLICKED
);
m
TouchHelp
er
.
sendEventFor
VirtualView
(
day
,
AccessibilityEvent
.
TYPE_VIEW_CLICKED
);
}
/**
...
...
@@ -518,7 +515,11 @@ public class SimpleMonthView extends View {
* has focus
*/
public
CalendarDay
getAccessibilityFocus
()
{
return
mNodeProvider
.
getFocusedItem
();
final
int
day
=
mTouchHelper
.
getFocusedVirtualView
();
if
(
day
>=
0
)
{
return
new
CalendarDay
(
mYear
,
mMonth
,
day
);
}
return
null
;
}
/**
...
...
@@ -526,7 +527,7 @@ public class SimpleMonthView extends View {
* contain accessibility focus.
*/
public
void
clearAccessibilityFocus
()
{
m
NodeProvid
er
.
clearFocused
Item
();
m
TouchHelp
er
.
clearFocused
VirtualView
();
}
/**
...
...
@@ -540,8 +541,7 @@ public class SimpleMonthView extends View {
if
((
day
.
year
!=
mYear
)
||
(
day
.
month
!=
mMonth
)
||
(
day
.
day
>
mNumCells
))
{
return
false
;
}
mNodeProvider
.
setFocusedItem
(
day
);
mTouchHelper
.
setFocusedVirtualView
(
day
.
day
);
return
true
;
}
...
...
@@ -549,104 +549,87 @@ public class SimpleMonthView extends View {
* Provides a virtual view hierarchy for interfacing with an accessibility
* service.
*/
private
class
MonthViewNodeProvider
extends
TouchExplorationHelper
<
CalendarDay
>
{
private
final
SparseArray
<
CalendarDay
>
mCachedItems
=
new
SparseArray
<
CalendarDay
>();
private
class
MonthViewTouchHelper
extends
ExploreByTouchHelper
{
private
static
final
String
DATE_FORMAT
=
"dd MMMM yyyy"
;
private
final
Rect
mTempRect
=
new
Rect
();
private
final
Calendar
mTempCalendar
=
Calendar
.
getInstance
();
Calendar
recycle
;
public
MonthViewTouchHelper
(
View
host
)
{
super
(
host
);
}
public
MonthViewNodeProvider
(
Context
context
,
View
parent
)
{
super
(
context
,
parent
);
public
void
setFocusedVirtualView
(
int
virtualViewId
)
{
getAccessibilityNodeProvider
(
SimpleMonthView
.
this
).
performAction
(
virtualViewId
,
AccessibilityNodeInfoCompat
.
ACTION_ACCESSIBILITY_FOCUS
,
null
);
}
@Override
public
void
invalidateItem
(
CalendarDay
item
)
{
super
.
invalidateItem
(
item
);
mCachedItems
.
delete
(
getIdForItem
(
item
));
public
void
clearFocusedVirtualView
()
{
final
int
focusedVirtualView
=
getFocusedVirtualView
();
if
(
focusedVirtualView
!=
ExploreByTouchHelper
.
INVALID_ID
)
{
getAccessibilityNodeProvider
(
SimpleMonthView
.
this
).
performAction
(
focusedVirtualView
,
AccessibilityNodeInfoCompat
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
,
null
);
}
}
@Override
public
void
invalidateParent
()
{
super
.
invalidateParent
();
mCachedItems
.
clear
();
protected
int
getVirtualViewAt
(
float
x
,
float
y
)
{
final
int
day
=
getDayFromLocation
(
x
,
y
);
if
(
day
>=
0
)
{
return
day
;
}
return
ExploreByTouchHelper
.
INVALID_ID
;
}
@Override
protected
boolean
performActionForItem
(
CalendarDay
item
,
int
action
,
Bundle
arguments
)
{
switch
(
action
)
{
case
AccessibilityNodeInfo
.
ACTION_CLICK
:
onDayClick
(
item
);
return
true
;
protected
void
getVisibleVirtualViews
(
List
<
Integer
>
virtualViewIds
)
{
for
(
int
day
=
1
;
day
<=
mNumCells
;
day
++)
{
virtualViewIds
.
add
(
day
);
}
return
false
;
}
@Override
protected
void
p
opulateEventFor
Item
(
CalendarDay
item
,
AccessibilityEvent
event
)
{
event
.
setContentDescription
(
getItemDescription
(
item
));
protected
void
onP
opulateEventFor
VirtualView
(
int
virtualViewId
,
AccessibilityEvent
event
)
{
event
.
setContentDescription
(
getItemDescription
(
virtualViewId
));
}
@Override
protected
void
p
opulateNodeFor
Item
(
CalendarDay
item
,
AccessibilityNodeInfoCompat
node
)
{
getItemBounds
(
item
,
mTempRect
);
protected
void
onP
opulateNodeFor
VirtualView
(
int
virtualViewId
,
AccessibilityNodeInfoCompat
node
)
{
getItemBounds
(
virtualViewId
,
mTempRect
);
node
.
setContentDescription
(
getItemDescription
(
item
));
node
.
setContentDescription
(
getItemDescription
(
virtualViewId
));
node
.
setBoundsInParent
(
mTempRect
);
node
.
addAction
(
AccessibilityNodeInfo
.
ACTION_CLICK
);
if
(
item
.
day
==
mSelectedDay
)
{
if
(
virtualViewId
==
mSelectedDay
)
{
node
.
setSelected
(
true
);
}
}
@Override
protected
void
getVisibleItems
(
List
<
CalendarDay
>
items
)
{
// TODO: Optimize, only return items visible within parent bounds.
for
(
int
day
=
1
;
day
<=
mNumCells
;
day
++)
{
items
.
add
(
getItemForId
(
day
));
}
}
@Override
protected
CalendarDay
getItemAt
(
float
x
,
float
y
)
{
return
getDayFromLocation
(
x
,
y
);
}
@Override
protected
int
getIdForItem
(
CalendarDay
item
)
{
return
item
.
day
;
}
@Override
protected
CalendarDay
getItemForId
(
int
id
)
{
if
((
id
<
1
)
||
(
id
>
mNumCells
))
{
return
null
;
}
final
CalendarDay
item
;
if
(
mCachedItems
.
indexOfKey
(
id
)
>=
0
)
{
item
=
mCachedItems
.
get
(
id
);
}
else
{
item
=
new
CalendarDay
(
mYear
,
mMonth
,
id
);
mCachedItems
.
put
(
id
,
item
);
protected
boolean
onPerformActionForVirtualView
(
int
virtualViewId
,
int
action
,
Bundle
arguments
)
{
switch
(
action
)
{
case
AccessibilityNodeInfo
.
ACTION_CLICK
:
onDayClick
(
virtualViewId
);
return
true
;
}
return
item
;
return
false
;
}
/**
* Calculates the bounding rectangle of a given time object.
*
* @param
item
The
time object
to calculate bounds for
* @param
day
The
day
to calculate bounds for
* @param rect The rectangle in which to store the bounds
*/
private
void
getItemBounds
(
CalendarDay
item
,
Rect
rect
)
{
private
void
getItemBounds
(
int
day
,
Rect
rect
)
{
final
int
offsetX
=
mPadding
;
final
int
offsetY
=
MONTH_HEADER_SIZE
;
final
int
cellHeight
=
mRowHeight
;
final
int
cellWidth
=
((
mWidth
-
(
2
*
mPadding
))
/
mNumDays
);
final
int
index
=
((
item
.
day
-
1
)
+
findDayOffset
());
final
int
index
=
((
day
-
1
)
+
findDayOffset
());
final
int
row
=
(
index
/
mNumDays
);
final
int
column
=
(
index
%
mNumDays
);
final
int
x
=
(
offsetX
+
(
column
*
cellWidth
));
...
...
@@ -660,17 +643,14 @@ public class SimpleMonthView extends View {
* description will be spoken, the components are ordered by descending
* specificity as DAY MONTH YEAR.
*
* @param
item
The
time object
to generate a description for
* @param
day
The
day
to generate a description for
* @return A description of the time object
*/
private
CharSequence
getItemDescription
(
CalendarDay
item
)
{
if
(
recycle
==
null
)
{
recycle
=
Calendar
.
getInstance
();
}
recycle
.
set
(
item
.
year
,
item
.
month
,
item
.
day
);
CharSequence
date
=
DateFormat
.
format
(
"dd MMMM yyyy"
,
recycle
.
getTimeInMillis
());
private
CharSequence
getItemDescription
(
int
day
)
{
mTempCalendar
.
set
(
mYear
,
mMonth
,
day
);
final
CharSequence
date
=
DateFormat
.
format
(
DATE_FORMAT
,
mTempCalendar
.
getTimeInMillis
());
if
(
item
.
day
==
mSelectedDay
)
{
if
(
day
==
mSelectedDay
)
{
return
getContext
().
getString
(
R
.
string
.
item_is_selected
,
date
);
}
...
...
This diff is collapsed.
Click to expand it.
src/com/googlecode/eyesfree/utils/TouchExplorationHelper.java
deleted
100644 → 0
View file @
d461f9bf
/*
* Copyright (C) 2012 Google Inc.
*
* 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.googlecode.eyesfree.utils
;
import
android.content.Context
;
import
android.graphics.Rect
;
import
android.os.Bundle
;
import
android.support.v4.view.AccessibilityDelegateCompat
;
import
android.support.v4.view.ViewCompat
;
import
android.support.v4.view.accessibility.AccessibilityNodeInfoCompat
;
import
android.support.v4.view.accessibility.AccessibilityNodeProviderCompat
;
import
android.support.v4.view.accessibility.AccessibilityRecordCompat
;
import
android.text.TextUtils
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.view.accessibility.AccessibilityEvent
;
import
android.view.accessibility.AccessibilityManager
;
import
java.util.LinkedList
;
import
java.util.List
;
public
abstract
class
TouchExplorationHelper
<
T
>
extends
AccessibilityNodeProviderCompat
implements
View
.
OnHoverListener
{
/** Virtual node identifier value for invalid nodes. */
public
static
final
int
INVALID_ID
=
Integer
.
MIN_VALUE
;
private
final
Rect
mTempScreenRect
=
new
Rect
();
private
final
Rect
mTempParentRect
=
new
Rect
();
private
final
Rect
mTempVisibleRect
=
new
Rect
();
private
final
int
[]
mTempGlobalRect
=
new
int
[
2
];
private
final
AccessibilityManager
mManager
;
private
View
mParentView
;
private
int
mFocusedItemId
=
INVALID_ID
;
private
T
mCurrentItem
=
null
;
/**
* Constructs a new touch exploration helper.
*
* @param context The parent context.
*/
public
TouchExplorationHelper
(
Context
context
,
View
parentView
)
{
mManager
=
(
AccessibilityManager
)
context
.
getSystemService
(
Context
.
ACCESSIBILITY_SERVICE
);
mParentView
=
parentView
;
}
/**
* @return The current accessibility focused item, or {@code null} if no
* item is focused.
*/
public
T
getFocusedItem
()
{
return
getItemForId
(
mFocusedItemId
);
}
/**
* Clears the current accessibility focused item.
*/
public
void
clearFocusedItem
()
{
final
int
itemId
=
mFocusedItemId
;
if
(
itemId
==
INVALID_ID
)
{
return
;
}
performAction
(
itemId
,
AccessibilityNodeInfoCompat
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
,
null
);
}
/**
* Requests accessibility focus be placed on the specified item.
*
* @param item The item to place focus on.
*/
public
void
setFocusedItem
(
T
item
)
{
final
int
itemId
=
getIdForItem
(
item
);
if
(
itemId
==
INVALID_ID
)
{
return
;
}
performAction
(
itemId
,
AccessibilityNodeInfoCompat
.
ACTION_ACCESSIBILITY_FOCUS
,
null
);
}
/**
* Invalidates cached information about the parent view.
* <p>
* You <b>must</b> call this method after adding or removing items from the
* parent view.
* </p>
*/
public
void
invalidateParent
()
{
mParentView
.
sendAccessibilityEvent
(
AccessibilityEvent
.
TYPE_WINDOW_CONTENT_CHANGED
);
}
/**
* Invalidates cached information for a particular item.
* <p>
* You <b>must</b> call this method when any of the properties set in
* {@link #populateNodeForItem(Object, AccessibilityNodeInfoCompat)} have
* changed.
* </p>
*
* @param item
*/
public
void
invalidateItem
(
T
item
)
{
sendEventForItem
(
item
,
AccessibilityEvent
.
TYPE_WINDOW_CONTENT_CHANGED
);
}
/**
* Populates an event of the specified type with information about an item
* and attempts to send it up through the view hierarchy.
*
* @param item The item for which to send an event.
* @param eventType The type of event to send.
* @return {@code true} if the event was sent successfully.
*/
public
boolean
sendEventForItem
(
T
item
,
int
eventType
)
{
if
(!
mManager
.
isEnabled
())
{
return
false
;
}
final
AccessibilityEvent
event
=
getEventForItem
(
item
,
eventType
);
final
ViewGroup
group
=
(
ViewGroup
)
mParentView
.
getParent
();
return
group
.
requestSendAccessibilityEvent
(
mParentView
,
event
);
}
@Override
public
AccessibilityNodeInfoCompat
createAccessibilityNodeInfo
(
int
virtualViewId
)
{
if
(
virtualViewId
==
View
.
NO_ID
)
{
return
getNodeForParent
();
}
final
T
item
=
getItemForId
(
virtualViewId
);
if
(
item
==
null
)
{
return
null
;
}
final
AccessibilityNodeInfoCompat
node
=
AccessibilityNodeInfoCompat
.
obtain
();
populateNodeForItemInternal
(
item
,
node
);
return
node
;
}
@Override
public
boolean
performAction
(
int
virtualViewId
,
int
action
,
Bundle
arguments
)
{
if
(
virtualViewId
==
View
.
NO_ID
)
{
return
ViewCompat
.
performAccessibilityAction
(
mParentView
,
action
,
arguments
);
}
final
T
item
=
getItemForId
(
virtualViewId
);
if
(
item
==
null
)
{
return
false
;
}
boolean
handled
=
false
;
switch
(
action
)
{
case
AccessibilityNodeInfoCompat
.
ACTION_ACCESSIBILITY_FOCUS
:
if
(
mFocusedItemId
!=
virtualViewId
)
{
mFocusedItemId
=
virtualViewId
;
sendEventForItem
(
item
,
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUSED
);
handled
=
true
;
}
break
;
case
AccessibilityNodeInfoCompat
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
:
if
(
mFocusedItemId
==
virtualViewId
)
{
mFocusedItemId
=
INVALID_ID
;
sendEventForItem
(
item
,
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
);
handled
=
true
;
}
break
;
}
handled
|=
performActionForItem
(
item
,
action
,
arguments
);
return
handled
;
}
@Override
public
boolean
onHover
(
View
view
,
MotionEvent
event
)
{
if
(!
mManager
.
isTouchExplorationEnabled
())
{
return
false
;
}
switch
(
event
.
getAction
())
{
case
MotionEvent
.
ACTION_HOVER_ENTER
:
case
MotionEvent
.
ACTION_HOVER_MOVE
:
final
T
item
=
getItemAt
(
event
.
getX
(),
event
.
getY
());
setCurrentItem
(
item
);
return
true
;
case
MotionEvent
.
ACTION_HOVER_EXIT
:
setCurrentItem
(
null
);
return
true
;
}
return
false
;
}
private
void
setCurrentItem
(
T
item
)
{
if
(
mCurrentItem
==
item
)
{
return
;
}
if
(
mCurrentItem
!=
null
)
{
sendEventForItem
(
mCurrentItem
,
AccessibilityEvent
.
TYPE_VIEW_HOVER_EXIT
);
}
mCurrentItem
=
item
;
if
(
mCurrentItem
!=
null
)
{
sendEventForItem
(
mCurrentItem
,
AccessibilityEvent
.
TYPE_VIEW_HOVER_ENTER
);
}
}
private
AccessibilityEvent
getEventForItem
(
T
item
,
int
eventType
)
{
final
AccessibilityEvent
event
=
AccessibilityEvent
.
obtain
(
eventType
);
final
AccessibilityRecordCompat
record
=
new
AccessibilityRecordCompat
(
event
);
final
int
virtualDescendantId
=
getIdForItem
(
item
);
// Ensure the client has good defaults.
event
.
setEnabled
(
true
);
// Allow the client to populate the event.
populateEventForItem
(
item
,
event
);
if
(
event
.
getText
().
isEmpty
()
&&
TextUtils
.
isEmpty
(
event
.
getContentDescription
()))
{
throw
new
RuntimeException
(
"You must add text or a content description in populateEventForItem()"
);
}
// Don't allow the client to override these properties.
event
.
setClassName
(
item
.
getClass
().
getName
());
event
.
setPackageName
(
mParentView
.
getContext
().
getPackageName
());
record
.
setSource
(
mParentView
,
virtualDescendantId
);
return
event
;
}
private
AccessibilityNodeInfoCompat
getNodeForParent
()
{
final
AccessibilityNodeInfoCompat
info
=
AccessibilityNodeInfoCompat
.
obtain
(
mParentView
);
ViewCompat
.
onInitializeAccessibilityNodeInfo
(
mParentView
,
info
);
final
LinkedList
<
T
>
items
=
new
LinkedList
<
T
>();
getVisibleItems
(
items
);
for
(
T
item
:
items
)
{
final
int
virtualDescendantId
=
getIdForItem
(
item
);
info
.
addChild
(
mParentView
,
virtualDescendantId
);
}
return
info
;
}
private
AccessibilityNodeInfoCompat
populateNodeForItemInternal
(
T
item
,
AccessibilityNodeInfoCompat
node
)
{
final
int
virtualDescendantId
=
getIdForItem
(
item
);
// Ensure the client has good defaults.
node
.
setEnabled
(
true
);
// Allow the client to populate the node.
populateNodeForItem
(
item
,
node
);
if
(
TextUtils
.
isEmpty
(
node
.
getText
())
&&
TextUtils
.
isEmpty
(
node
.
getContentDescription
()))
{
throw
new
RuntimeException
(
"You must add text or a content description in populateNodeForItem()"
);
}
// Don't allow the client to override these properties.
node
.
setPackageName
(
mParentView
.
getContext
().
getPackageName
());
node
.
setClassName
(
item
.
getClass
().
getName
());
node
.
setParent
(
mParentView
);
node
.
setSource
(
mParentView
,
virtualDescendantId
);
if
(
mFocusedItemId
==
virtualDescendantId
)
{
node
.
addAction
(
AccessibilityNodeInfoCompat
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
);
}
else
{
node
.
addAction
(
AccessibilityNodeInfoCompat
.
ACTION_ACCESSIBILITY_FOCUS
);
}
node
.
getBoundsInParent
(
mTempParentRect
);
if
(
mTempParentRect
.
isEmpty
())
{
throw
new
RuntimeException
(
"You must set parent bounds in populateNodeForItem()"
);
}
// Set the visibility based on the parent bound.
if
(
intersectVisibleToUser
(
mTempParentRect
))
{
node
.
setVisibleToUser
(
true
);
node
.
setBoundsInParent
(
mTempParentRect
);
}
// Calculate screen-relative bound.
mParentView
.
getLocationOnScreen
(
mTempGlobalRect
);
final
int
offsetX
=
mTempGlobalRect
[
0
];
final
int
offsetY
=
mTempGlobalRect
[
1
];
mTempScreenRect
.
set
(
mTempParentRect
);
mTempScreenRect
.
offset
(
offsetX
,
offsetY
);
node
.
setBoundsInScreen
(
mTempScreenRect
);
return
node
;
}
/**
* Computes whether the specified {@link Rect} intersects with the visible
* portion of its parent {@link View}. Modifies {@code localRect} to
* contain only the visible portion.
*
* @param localRect A rectangle in local (parent) coordinates.
* @return Whether the specified {@link Rect} is visible on the screen.
*/
private
boolean
intersectVisibleToUser
(
Rect
localRect
)
{
// Missing or empty bounds mean this view is not visible.
if
((
localRect
==
null
)
||
localRect
.
isEmpty
())
{
return
false
;
}
// Attached to invisible window means this view is not visible.
if
(
mParentView
.
getWindowVisibility
()
!=
View
.
VISIBLE
)
{
return
false
;
}
// An invisible predecessor or one with alpha zero means
// that this view is not visible to the user.
Object
current
=
this
;
while
(
current
instanceof
View
)
{
final
View
view
=
(
View
)
current
;
// We have attach info so this view is attached and there is no
// need to check whether we reach to ViewRootImpl on the way up.
if
((
view
.
getAlpha
()
<=
0
)
||
(
view
.
getVisibility
()
!=
View
.
VISIBLE
))
{
return
false
;
}
current
=
view
.
getParent
();
}
// If no portion of the parent is visible, this view is not visible.
if
(!
mParentView
.
getLocalVisibleRect
(
mTempVisibleRect
))
{
return
false
;
}
// Check if the view intersects the visible portion of the parent.
return
localRect
.
intersect
(
mTempVisibleRect
);
}
public
AccessibilityDelegateCompat
getAccessibilityDelegate
()
{
return
mDelegate
;
}
private
final
AccessibilityDelegateCompat
mDelegate
=
new
AccessibilityDelegateCompat
()
{
@Override
public
void
onInitializeAccessibilityEvent
(
View
view
,
AccessibilityEvent
event
)
{
super
.
onInitializeAccessibilityEvent
(
view
,
event
);
event
.
setClassName
(
view
.
getClass
().
getName
());
}
@Override
public
void
onInitializeAccessibilityNodeInfo
(
View
view
,
AccessibilityNodeInfoCompat
info
)
{
super
.
onInitializeAccessibilityNodeInfo
(
view
,
info
);
info
.
setClassName
(
view
.
getClass
().
getName
());
}
@Override
public
AccessibilityNodeProviderCompat
getAccessibilityNodeProvider
(
View
host
)
{
return
TouchExplorationHelper
.
this
;
}
};
/**
* Performs an accessibility action on the specified item. See
* {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)}.
* <p>
* The helper class automatically handles focus management resulting from
* {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS} and
* {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}, so
* typically a developer only needs to handle actions added manually in the
* {{@link #populateNodeForItem(Object, AccessibilityNodeInfoCompat)}
* method.
* </p>
*
* @param item The item on which to perform the action.
* @param action The accessibility action to perform.
* @param arguments Arguments for the action, or optionally {@code null}.
* @return {@code true} if the action was performed successfully.
*/
protected
abstract
boolean
performActionForItem
(
T
item
,
int
action
,
Bundle
arguments
);
/**
* Populates an event with information about the specified item.
* <p>
* At a minimum, a developer must populate the event text by doing one of
* the following:
* <ul>
* <li>appending text to {@link AccessibilityEvent#getText()}</li>
* <li>populating a description with
* {@link AccessibilityEvent#setContentDescription(CharSequence)}</li>
* </ul>
* </p>
*
* @param item The item for which to populate the event.
* @param event The event to populate.
*/
protected
abstract
void
populateEventForItem
(
T
item
,
AccessibilityEvent
event
);
/**
* Populates a node with information about the specified item.
* <p>
* At a minimum, a developer must:
* <ul>
* <li>populate the event text using
* {@link AccessibilityNodeInfoCompat#setText(CharSequence)} or
* {@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)}
* </li>
* <li>set the item's parent-relative bounds using
* {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)}
* </ul>
*
* @param item The item for which to populate the node.
* @param node The node to populate.
*/
protected
abstract
void
populateNodeForItem
(
T
item
,
AccessibilityNodeInfoCompat
node
);
/**
* Populates a list with the parent view's visible items.
* <p>
* The result of this method is cached until the developer calls
* {@link #invalidateParent()}.
* </p>
*
* @param items The list to populate with visible items.
*/
protected
abstract
void
getVisibleItems
(
List
<
T
>
items
);
/**
* Returns the item under the specified parent-relative coordinates.
*
* @param x The parent-relative x coordinate.
* @param y The parent-relative y coordinate.
* @return The item under coordinates (x,y).
*/
protected
abstract
T
getItemAt
(
float
x
,
float
y
);
/**
* Returns the unique identifier for an item. If the specified item does not
* exist, returns {@link #INVALID_ID}.
* <p>
* This result of this method must be consistent with
* {@link #getItemForId(int)}.
* </p>
*
* @param item The item whose identifier to return.
* @return A unique identifier, or {@link #INVALID_ID}.
*/
protected
abstract
int
getIdForItem
(
T
item
);
/**
* Returns the item for a unique identifier. If the specified item does not
* exist, returns {@code null}.
*
* @param id The identifier for the item to return.
* @return An item, or {@code null}.
*/
protected
abstract
T
getItemForId
(
int
id
);
}
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment