Loading api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -25088,6 +25088,7 @@ package android.view.accessibility { method public void appendRecord(android.view.accessibility.AccessibilityRecord); method public int describeContents(); method public static java.lang.String eventTypeToString(int); method public int getAction(); method public long getEventTime(); method public int getEventType(); method public int getMovementGranularity(); Loading @@ -25098,6 +25099,7 @@ package android.view.accessibility { method public static android.view.accessibility.AccessibilityEvent obtain(int); method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent); method public static android.view.accessibility.AccessibilityEvent obtain(); method public void setAction(int); method public void setEventTime(long); method public void setEventType(int); method public void setMovementGranularity(int); core/java/android/view/AccessibilityIterators.java 0 → 100644 +352 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 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.view; import android.content.ComponentCallbacks; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import java.text.BreakIterator; import java.util.Locale; /** * This class contains the implementation of text segment iterators * for accessibility support. * * Note: Such iterators are needed in the view package since we want * to be able to iterator over content description of any view. * * @hide */ public final class AccessibilityIterators { /** * @hide */ public static interface TextSegmentIterator { public int[] following(int current); public int[] preceding(int current); } /** * @hide */ public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator { protected static final int DONE = -1; protected String mText; private final int[] mSegment = new int[2]; public void initialize(String text) { mText = text; } protected int[] getRange(int start, int end) { if (start < 0 || end < 0 || start == end) { return null; } mSegment[0] = start; mSegment[1] = end; return mSegment; } } static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator implements ComponentCallbacks { private static CharacterTextSegmentIterator sInstance; private final Context mAppContext; protected BreakIterator mImpl; public static CharacterTextSegmentIterator getInstance(Context context) { if (sInstance == null) { sInstance = new CharacterTextSegmentIterator(context); } return sInstance; } private CharacterTextSegmentIterator(Context context) { mAppContext = context.getApplicationContext(); Locale locale = mAppContext.getResources().getConfiguration().locale; onLocaleChanged(locale); ViewRootImpl.addConfigCallback(this); } @Override public void initialize(String text) { super.initialize(text); mImpl.setText(text); } @Override public int[] following(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset >= textLegth) { return null; } int start = -1; if (offset < 0) { offset = 0; if (mImpl.isBoundary(offset)) { start = offset; } } if (start < 0) { start = mImpl.following(offset); } if (start < 0) { return null; } final int end = mImpl.following(start); return getRange(start, end); } @Override public int[] preceding(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset <= 0) { return null; } int end = -1; if (offset > mText.length()) { offset = mText.length(); if (mImpl.isBoundary(offset)) { end = offset; } } if (end < 0) { end = mImpl.preceding(offset); } if (end < 0) { return null; } final int start = mImpl.preceding(end); return getRange(start, end); } @Override public void onConfigurationChanged(Configuration newConfig) { Configuration oldConfig = mAppContext.getResources().getConfiguration(); final int changed = oldConfig.diff(newConfig); if ((changed & ActivityInfo.CONFIG_LOCALE) != 0) { Locale locale = newConfig.locale; onLocaleChanged(locale); } } @Override public void onLowMemory() { /* ignore */ } protected void onLocaleChanged(Locale locale) { mImpl = BreakIterator.getCharacterInstance(locale); } } static class WordTextSegmentIterator extends CharacterTextSegmentIterator { private static WordTextSegmentIterator sInstance; public static WordTextSegmentIterator getInstance(Context context) { if (sInstance == null) { sInstance = new WordTextSegmentIterator(context); } return sInstance; } private WordTextSegmentIterator(Context context) { super(context); } @Override protected void onLocaleChanged(Locale locale) { mImpl = BreakIterator.getWordInstance(locale); } @Override public int[] following(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset >= mText.length()) { return null; } int start = -1; if (offset < 0) { offset = 0; if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) { start = offset; } } if (start < 0) { while ((offset = mImpl.following(offset)) != DONE) { if (isLetterOrDigit(offset)) { start = offset; break; } } } if (start < 0) { return null; } final int end = mImpl.following(start); return getRange(start, end); } @Override public int[] preceding(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset <= 0) { return null; } int end = -1; if (offset > mText.length()) { offset = mText.length(); if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) { end = offset; } } if (end < 0) { while ((offset = mImpl.preceding(offset)) != DONE) { if (offset > 0 && isLetterOrDigit(offset - 1)) { end = offset; break; } } } if (end < 0) { return null; } final int start = mImpl.preceding(end); return getRange(start, end); } private boolean isLetterOrDigit(int index) { if (index >= 0 && index < mText.length()) { final int codePoint = mText.codePointAt(index); return Character.isLetterOrDigit(codePoint); } return false; } } static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator { private static ParagraphTextSegmentIterator sInstance; public static ParagraphTextSegmentIterator getInstance() { if (sInstance == null) { sInstance = new ParagraphTextSegmentIterator(); } return sInstance; } @Override public int[] following(int offset) { final int textLength = mText.length(); if (textLength <= 0) { return null; } if (offset >= textLength) { return null; } int start = -1; if (offset < 0) { start = 0; } else { for (int i = offset + 1; i < textLength; i++) { if (mText.charAt(i) == '\n') { start = i; break; } } } while (start < textLength && mText.charAt(start) == '\n') { start++; } if (start < 0) { return null; } int end = start; for (int i = end + 1; i < textLength; i++) { end = i; if (mText.charAt(i) == '\n') { break; } } while (end < textLength && mText.charAt(end) == '\n') { end++; } return getRange(start, end); } @Override public int[] preceding(int offset) { final int textLength = mText.length(); if (textLength <= 0) { return null; } if (offset <= 0) { return null; } int end = -1; if (offset > mText.length()) { end = mText.length(); } else { if (offset > 0 && mText.charAt(offset - 1) == '\n') { offset--; } for (int i = offset - 1; i >= 0; i--) { if (i > 0 && mText.charAt(i - 1) == '\n') { end = i; break; } } } if (end <= 0) { return null; } int start = end; while (start > 0 && mText.charAt(start - 1) == '\n') { start--; } if (start == 0 && mText.charAt(start) == '\n') { return null; } for (int i = start - 1; i >= 0; i--) { start = i; if (start > 0 && mText.charAt(i - 1) == '\n') { break; } } start = Math.max(0, start); return getRange(start, end); } } } core/java/android/view/View.java +162 −6 Original line number Diff line number Diff line Loading @@ -47,7 +47,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.LocaleUtil; Loading @@ -60,6 +59,10 @@ import android.util.Property; import android.util.SparseArray; import android.util.TypedValue; import android.view.ContextMenu.ContextMenuInfo; import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.AccessibilityIterators.ParagraphTextSegmentIterator; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; Loading Loading @@ -1524,7 +1527,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED; | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; /** * Temporary Rect currently for use in setBackground(). This will probably Loading Loading @@ -1589,6 +1593,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ int mAccessibilityViewId = NO_ID; /** * @hide */ private int mAccessibilityCursorPosition = -1; /** * The view's tag. * {@hide} Loading Loading @@ -4714,11 +4723,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); } if (getContentDescription() != null) { if (mContentDescription != null && mContentDescription.length() > 0) { info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD); | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH); } } Loading Loading @@ -5949,7 +5959,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal outViews.add(this); } } else if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0 && !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mContentDescription)) { && (searched != null && searched.length() > 0) && (mContentDescription != null && mContentDescription.length() > 0)) { String searchedLowerCase = searched.toString().toLowerCase(); String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase(); if (contentDescriptionLowerCase.contains(searchedLowerCase)) { Loading Loading @@ -6050,6 +6061,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal invalidate(); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); notifyAccessibilityStateChanged(); // Clear the text navigation state. setAccessibilityCursorPosition(-1); // Try to move accessibility focus to the input focus. View rootView = getRootView(); if (rootView != null) { Loading Loading @@ -6447,9 +6462,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * possible accessibility actions look at {@link AccessibilityNodeInfo}. * * @param action The action to perform. * @param arguments Optional action arguments. * @return Whether the action was performed. */ public boolean performAccessibilityAction(int action, Bundle args) { public boolean performAccessibilityAction(int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { Loading Loading @@ -6498,9 +6514,149 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return true; } } break; case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: { if (arguments != null) { final int granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); return nextAtGranularity(granularity); } } break; case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: { if (arguments != null) { final int granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); return previousAtGranularity(granularity); } } break; } return false; } private boolean nextAtGranularity(int granularity) { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { return false; } TextSegmentIterator iterator = getIteratorForGranularity(granularity); if (iterator == null) { return false; } final int current = getAccessibilityCursorPosition(); final int[] range = iterator.following(current); if (range == null) { setAccessibilityCursorPosition(-1); return false; } final int start = range[0]; final int end = range[1]; setAccessibilityCursorPosition(start); sendViewTextTraversedAtGranularityEvent( AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, granularity, start, end); return true; } private boolean previousAtGranularity(int granularity) { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { return false; } TextSegmentIterator iterator = getIteratorForGranularity(granularity); if (iterator == null) { return false; } final int selectionStart = getAccessibilityCursorPosition(); final int current = selectionStart >= 0 ? selectionStart : text.length() + 1; final int[] range = iterator.preceding(current); if (range == null) { setAccessibilityCursorPosition(-1); return false; } final int start = range[0]; final int end = range[1]; setAccessibilityCursorPosition(end); sendViewTextTraversedAtGranularityEvent( AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, granularity, start, end); return true; } /** * Gets the text reported for accessibility purposes. * * @return The accessibility text. * * @hide */ public CharSequence getIterableTextForAccessibility() { return mContentDescription; } /** * @hide */ public int getAccessibilityCursorPosition() { return mAccessibilityCursorPosition; } /** * @hide */ public void setAccessibilityCursorPosition(int position) { mAccessibilityCursorPosition = position; } private void sendViewTextTraversedAtGranularityEvent(int action, int granularity, int fromIndex, int toIndex) { if (mParent == null) { return; } AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); onInitializeAccessibilityEvent(event); onPopulateAccessibilityEvent(event); event.setFromIndex(fromIndex); event.setToIndex(toIndex); event.setAction(action); event.setMovementGranularity(granularity); mParent.requestSendAccessibilityEvent(this, event); } /** * @hide */ public TextSegmentIterator getIteratorForGranularity(int granularity) { switch (granularity) { case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { CharacterTextSegmentIterator iterator = CharacterTextSegmentIterator.getInstance(mContext); iterator.initialize(text.toString()); return iterator; } } break; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { WordTextSegmentIterator iterator = WordTextSegmentIterator.getInstance(mContext); iterator.initialize(text.toString()); return iterator; } } break; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH: { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { ParagraphTextSegmentIterator iterator = ParagraphTextSegmentIterator.getInstance(); iterator.initialize(text.toString()); return iterator; } } break; } return null; } /** * @hide Loading core/java/android/view/accessibility/AccessibilityEvent.java +35 −1 Original line number Diff line number Diff line Loading @@ -236,12 +236,19 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the current text at the movement granularity.</li> * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text * was traversed.</li> * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #getFromIndex()} - The start of the next/previous text at the specified granularity * - inclusive.</li> * <li>{@link #getToIndex()} - The end of the next/previous text at the specified granularity * - exclusive.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #getContentDescription()} - The content description of the source.</li> * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text * was traversed.</li> * <li>{@link #getAction()} - Gets traversal action which specifies the direction.</li> * </ul> * </p> * <p> Loading Loading @@ -635,6 +642,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private CharSequence mPackageName; private long mEventTime; int mMovementGranularity; int mAction; private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); Loading @@ -653,6 +661,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par super.init(event); mEventType = event.mEventType; mMovementGranularity = event.mMovementGranularity; mAction = event.mAction; mEventTime = event.mEventTime; mPackageName = event.mPackageName; } Loading Loading @@ -790,6 +799,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par return mMovementGranularity; } /** * Sets the performed action that triggered this event. * * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setAction(int action) { enforceNotSealed(); mAction = action; } /** * Gets the performed action that triggered this event. * * @return The action. */ public int getAction() { return mAction; } /** * Returns a cached instance if such is available or a new one is * instantiated with its type property set. Loading Loading @@ -879,6 +909,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par super.clear(); mEventType = 0; mMovementGranularity = 0; mAction = 0; mPackageName = null; mEventTime = 0; while (!mRecords.isEmpty()) { Loading @@ -896,6 +927,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mSealed = (parcel.readInt() == 1); mEventType = parcel.readInt(); mMovementGranularity = parcel.readInt(); mAction = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); mConnectionId = parcel.readInt(); Loading Loading @@ -947,6 +979,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); parcel.writeInt(mMovementGranularity); parcel.writeInt(mAction); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); parcel.writeInt(mConnectionId); Loading Loading @@ -1004,6 +1037,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); builder.append("; MovementGranularity: ").append(mMovementGranularity); builder.append("; Action: ").append(mAction); builder.append(super.toString()); if (DEBUG) { builder.append("\n"); Loading core/java/android/view/accessibility/AccessibilityNodeInfo.java +2 −2 Original line number Diff line number Diff line Loading @@ -102,12 +102,12 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_CLEAR_SELECTION = 0x00000008; /** * Action that long clicks on the node info. * Action that clicks on the node info. */ public static final int ACTION_CLICK = 0x00000010; /** * Action that clicks on the node. * Action that long clicks on the node. */ public static final int ACTION_LONG_CLICK = 0x00000020; Loading Loading
api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -25088,6 +25088,7 @@ package android.view.accessibility { method public void appendRecord(android.view.accessibility.AccessibilityRecord); method public int describeContents(); method public static java.lang.String eventTypeToString(int); method public int getAction(); method public long getEventTime(); method public int getEventType(); method public int getMovementGranularity(); Loading @@ -25098,6 +25099,7 @@ package android.view.accessibility { method public static android.view.accessibility.AccessibilityEvent obtain(int); method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent); method public static android.view.accessibility.AccessibilityEvent obtain(); method public void setAction(int); method public void setEventTime(long); method public void setEventType(int); method public void setMovementGranularity(int);
core/java/android/view/AccessibilityIterators.java 0 → 100644 +352 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 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.view; import android.content.ComponentCallbacks; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import java.text.BreakIterator; import java.util.Locale; /** * This class contains the implementation of text segment iterators * for accessibility support. * * Note: Such iterators are needed in the view package since we want * to be able to iterator over content description of any view. * * @hide */ public final class AccessibilityIterators { /** * @hide */ public static interface TextSegmentIterator { public int[] following(int current); public int[] preceding(int current); } /** * @hide */ public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator { protected static final int DONE = -1; protected String mText; private final int[] mSegment = new int[2]; public void initialize(String text) { mText = text; } protected int[] getRange(int start, int end) { if (start < 0 || end < 0 || start == end) { return null; } mSegment[0] = start; mSegment[1] = end; return mSegment; } } static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator implements ComponentCallbacks { private static CharacterTextSegmentIterator sInstance; private final Context mAppContext; protected BreakIterator mImpl; public static CharacterTextSegmentIterator getInstance(Context context) { if (sInstance == null) { sInstance = new CharacterTextSegmentIterator(context); } return sInstance; } private CharacterTextSegmentIterator(Context context) { mAppContext = context.getApplicationContext(); Locale locale = mAppContext.getResources().getConfiguration().locale; onLocaleChanged(locale); ViewRootImpl.addConfigCallback(this); } @Override public void initialize(String text) { super.initialize(text); mImpl.setText(text); } @Override public int[] following(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset >= textLegth) { return null; } int start = -1; if (offset < 0) { offset = 0; if (mImpl.isBoundary(offset)) { start = offset; } } if (start < 0) { start = mImpl.following(offset); } if (start < 0) { return null; } final int end = mImpl.following(start); return getRange(start, end); } @Override public int[] preceding(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset <= 0) { return null; } int end = -1; if (offset > mText.length()) { offset = mText.length(); if (mImpl.isBoundary(offset)) { end = offset; } } if (end < 0) { end = mImpl.preceding(offset); } if (end < 0) { return null; } final int start = mImpl.preceding(end); return getRange(start, end); } @Override public void onConfigurationChanged(Configuration newConfig) { Configuration oldConfig = mAppContext.getResources().getConfiguration(); final int changed = oldConfig.diff(newConfig); if ((changed & ActivityInfo.CONFIG_LOCALE) != 0) { Locale locale = newConfig.locale; onLocaleChanged(locale); } } @Override public void onLowMemory() { /* ignore */ } protected void onLocaleChanged(Locale locale) { mImpl = BreakIterator.getCharacterInstance(locale); } } static class WordTextSegmentIterator extends CharacterTextSegmentIterator { private static WordTextSegmentIterator sInstance; public static WordTextSegmentIterator getInstance(Context context) { if (sInstance == null) { sInstance = new WordTextSegmentIterator(context); } return sInstance; } private WordTextSegmentIterator(Context context) { super(context); } @Override protected void onLocaleChanged(Locale locale) { mImpl = BreakIterator.getWordInstance(locale); } @Override public int[] following(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset >= mText.length()) { return null; } int start = -1; if (offset < 0) { offset = 0; if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) { start = offset; } } if (start < 0) { while ((offset = mImpl.following(offset)) != DONE) { if (isLetterOrDigit(offset)) { start = offset; break; } } } if (start < 0) { return null; } final int end = mImpl.following(start); return getRange(start, end); } @Override public int[] preceding(int offset) { final int textLegth = mText.length(); if (textLegth <= 0) { return null; } if (offset <= 0) { return null; } int end = -1; if (offset > mText.length()) { offset = mText.length(); if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) { end = offset; } } if (end < 0) { while ((offset = mImpl.preceding(offset)) != DONE) { if (offset > 0 && isLetterOrDigit(offset - 1)) { end = offset; break; } } } if (end < 0) { return null; } final int start = mImpl.preceding(end); return getRange(start, end); } private boolean isLetterOrDigit(int index) { if (index >= 0 && index < mText.length()) { final int codePoint = mText.codePointAt(index); return Character.isLetterOrDigit(codePoint); } return false; } } static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator { private static ParagraphTextSegmentIterator sInstance; public static ParagraphTextSegmentIterator getInstance() { if (sInstance == null) { sInstance = new ParagraphTextSegmentIterator(); } return sInstance; } @Override public int[] following(int offset) { final int textLength = mText.length(); if (textLength <= 0) { return null; } if (offset >= textLength) { return null; } int start = -1; if (offset < 0) { start = 0; } else { for (int i = offset + 1; i < textLength; i++) { if (mText.charAt(i) == '\n') { start = i; break; } } } while (start < textLength && mText.charAt(start) == '\n') { start++; } if (start < 0) { return null; } int end = start; for (int i = end + 1; i < textLength; i++) { end = i; if (mText.charAt(i) == '\n') { break; } } while (end < textLength && mText.charAt(end) == '\n') { end++; } return getRange(start, end); } @Override public int[] preceding(int offset) { final int textLength = mText.length(); if (textLength <= 0) { return null; } if (offset <= 0) { return null; } int end = -1; if (offset > mText.length()) { end = mText.length(); } else { if (offset > 0 && mText.charAt(offset - 1) == '\n') { offset--; } for (int i = offset - 1; i >= 0; i--) { if (i > 0 && mText.charAt(i - 1) == '\n') { end = i; break; } } } if (end <= 0) { return null; } int start = end; while (start > 0 && mText.charAt(start - 1) == '\n') { start--; } if (start == 0 && mText.charAt(start) == '\n') { return null; } for (int i = start - 1; i >= 0; i--) { start = i; if (start > 0 && mText.charAt(i - 1) == '\n') { break; } } start = Math.max(0, start); return getRange(start, end); } } }
core/java/android/view/View.java +162 −6 Original line number Diff line number Diff line Loading @@ -47,7 +47,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.LocaleUtil; Loading @@ -60,6 +59,10 @@ import android.util.Property; import android.util.SparseArray; import android.util.TypedValue; import android.view.ContextMenu.ContextMenuInfo; import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.AccessibilityIterators.ParagraphTextSegmentIterator; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; Loading Loading @@ -1524,7 +1527,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED; | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; /** * Temporary Rect currently for use in setBackground(). This will probably Loading Loading @@ -1589,6 +1593,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ int mAccessibilityViewId = NO_ID; /** * @hide */ private int mAccessibilityCursorPosition = -1; /** * The view's tag. * {@hide} Loading Loading @@ -4714,11 +4723,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); } if (getContentDescription() != null) { if (mContentDescription != null && mContentDescription.length() > 0) { info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD); | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH); } } Loading Loading @@ -5949,7 +5959,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal outViews.add(this); } } else if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0 && !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mContentDescription)) { && (searched != null && searched.length() > 0) && (mContentDescription != null && mContentDescription.length() > 0)) { String searchedLowerCase = searched.toString().toLowerCase(); String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase(); if (contentDescriptionLowerCase.contains(searchedLowerCase)) { Loading Loading @@ -6050,6 +6061,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal invalidate(); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); notifyAccessibilityStateChanged(); // Clear the text navigation state. setAccessibilityCursorPosition(-1); // Try to move accessibility focus to the input focus. View rootView = getRootView(); if (rootView != null) { Loading Loading @@ -6447,9 +6462,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * possible accessibility actions look at {@link AccessibilityNodeInfo}. * * @param action The action to perform. * @param arguments Optional action arguments. * @return Whether the action was performed. */ public boolean performAccessibilityAction(int action, Bundle args) { public boolean performAccessibilityAction(int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { Loading Loading @@ -6498,9 +6514,149 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return true; } } break; case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: { if (arguments != null) { final int granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); return nextAtGranularity(granularity); } } break; case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: { if (arguments != null) { final int granularity = arguments.getInt( AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); return previousAtGranularity(granularity); } } break; } return false; } private boolean nextAtGranularity(int granularity) { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { return false; } TextSegmentIterator iterator = getIteratorForGranularity(granularity); if (iterator == null) { return false; } final int current = getAccessibilityCursorPosition(); final int[] range = iterator.following(current); if (range == null) { setAccessibilityCursorPosition(-1); return false; } final int start = range[0]; final int end = range[1]; setAccessibilityCursorPosition(start); sendViewTextTraversedAtGranularityEvent( AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, granularity, start, end); return true; } private boolean previousAtGranularity(int granularity) { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { return false; } TextSegmentIterator iterator = getIteratorForGranularity(granularity); if (iterator == null) { return false; } final int selectionStart = getAccessibilityCursorPosition(); final int current = selectionStart >= 0 ? selectionStart : text.length() + 1; final int[] range = iterator.preceding(current); if (range == null) { setAccessibilityCursorPosition(-1); return false; } final int start = range[0]; final int end = range[1]; setAccessibilityCursorPosition(end); sendViewTextTraversedAtGranularityEvent( AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, granularity, start, end); return true; } /** * Gets the text reported for accessibility purposes. * * @return The accessibility text. * * @hide */ public CharSequence getIterableTextForAccessibility() { return mContentDescription; } /** * @hide */ public int getAccessibilityCursorPosition() { return mAccessibilityCursorPosition; } /** * @hide */ public void setAccessibilityCursorPosition(int position) { mAccessibilityCursorPosition = position; } private void sendViewTextTraversedAtGranularityEvent(int action, int granularity, int fromIndex, int toIndex) { if (mParent == null) { return; } AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); onInitializeAccessibilityEvent(event); onPopulateAccessibilityEvent(event); event.setFromIndex(fromIndex); event.setToIndex(toIndex); event.setAction(action); event.setMovementGranularity(granularity); mParent.requestSendAccessibilityEvent(this, event); } /** * @hide */ public TextSegmentIterator getIteratorForGranularity(int granularity) { switch (granularity) { case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { CharacterTextSegmentIterator iterator = CharacterTextSegmentIterator.getInstance(mContext); iterator.initialize(text.toString()); return iterator; } } break; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { WordTextSegmentIterator iterator = WordTextSegmentIterator.getInstance(mContext); iterator.initialize(text.toString()); return iterator; } } break; case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH: { CharSequence text = getIterableTextForAccessibility(); if (text != null && text.length() > 0) { ParagraphTextSegmentIterator iterator = ParagraphTextSegmentIterator.getInstance(); iterator.initialize(text.toString()); return iterator; } } break; } return null; } /** * @hide Loading
core/java/android/view/accessibility/AccessibilityEvent.java +35 −1 Original line number Diff line number Diff line Loading @@ -236,12 +236,19 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the current text at the movement granularity.</li> * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text * was traversed.</li> * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #getFromIndex()} - The start of the next/previous text at the specified granularity * - inclusive.</li> * <li>{@link #getToIndex()} - The end of the next/previous text at the specified granularity * - exclusive.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #getContentDescription()} - The content description of the source.</li> * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text * was traversed.</li> * <li>{@link #getAction()} - Gets traversal action which specifies the direction.</li> * </ul> * </p> * <p> Loading Loading @@ -635,6 +642,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private CharSequence mPackageName; private long mEventTime; int mMovementGranularity; int mAction; private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); Loading @@ -653,6 +661,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par super.init(event); mEventType = event.mEventType; mMovementGranularity = event.mMovementGranularity; mAction = event.mAction; mEventTime = event.mEventTime; mPackageName = event.mPackageName; } Loading Loading @@ -790,6 +799,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par return mMovementGranularity; } /** * Sets the performed action that triggered this event. * * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setAction(int action) { enforceNotSealed(); mAction = action; } /** * Gets the performed action that triggered this event. * * @return The action. */ public int getAction() { return mAction; } /** * Returns a cached instance if such is available or a new one is * instantiated with its type property set. Loading Loading @@ -879,6 +909,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par super.clear(); mEventType = 0; mMovementGranularity = 0; mAction = 0; mPackageName = null; mEventTime = 0; while (!mRecords.isEmpty()) { Loading @@ -896,6 +927,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mSealed = (parcel.readInt() == 1); mEventType = parcel.readInt(); mMovementGranularity = parcel.readInt(); mAction = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); mConnectionId = parcel.readInt(); Loading Loading @@ -947,6 +979,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); parcel.writeInt(mMovementGranularity); parcel.writeInt(mAction); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); parcel.writeInt(mConnectionId); Loading Loading @@ -1004,6 +1037,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); builder.append("; MovementGranularity: ").append(mMovementGranularity); builder.append("; Action: ").append(mAction); builder.append(super.toString()); if (DEBUG) { builder.append("\n"); Loading
core/java/android/view/accessibility/AccessibilityNodeInfo.java +2 −2 Original line number Diff line number Diff line Loading @@ -102,12 +102,12 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_CLEAR_SELECTION = 0x00000008; /** * Action that long clicks on the node info. * Action that clicks on the node info. */ public static final int ACTION_CLICK = 0x00000010; /** * Action that clicks on the node. * Action that long clicks on the node. */ public static final int ACTION_LONG_CLICK = 0x00000020; Loading