diff options
Diffstat (limited to 'src/com/android/settings/widget/LinkAccessibilityHelper.java')
-rw-r--r-- | src/com/android/settings/widget/LinkAccessibilityHelper.java | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/com/android/settings/widget/LinkAccessibilityHelper.java b/src/com/android/settings/widget/LinkAccessibilityHelper.java new file mode 100644 index 000000000..2d4d585f9 --- /dev/null +++ b/src/com/android/settings/widget/LinkAccessibilityHelper.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2014 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.settings.widget; + +import android.graphics.Rect; +import android.os.Bundle; +import android.text.Layout; +import android.text.Spanned; +import android.text.style.ClickableSpan; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.TextView; + +import java.util.List; + +/** + * Copied from setup wizard. + */ +public class LinkAccessibilityHelper extends ExploreByTouchHelper { + + private static final String TAG = "LinkAccessibilityHelper"; + + private final TextView mView; + private final Rect mTempRect = new Rect(); + + public LinkAccessibilityHelper(TextView view) { + super(view); + mView = view; + } + + @Override + protected int getVirtualViewAt(float x, float y) { + final CharSequence text = mView.getText(); + if (text instanceof Spanned) { + final Spanned spannedText = (Spanned) text; + final int offset = mView.getOffsetForPosition(x, y); + ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class); + if (linkSpans.length == 1) { + ClickableSpan linkSpan = linkSpans[0]; + return spannedText.getSpanStart(linkSpan); + } + } + return INVALID_ID; + } + + @Override + protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { + final CharSequence text = mView.getText(); + if (text instanceof Spanned) { + final Spanned spannedText = (Spanned) text; + ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(), + ClickableSpan.class); + for (ClickableSpan span : linkSpans) { + virtualViewIds.add(spannedText.getSpanStart(span)); + } + } + } + + @Override + protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { + final ClickableSpan span = getSpanForOffset(virtualViewId); + if (span != null) { + event.setContentDescription(getTextForSpan(span)); + } else { + Log.e(TAG, "ClickableSpan is null for offset: " + virtualViewId); + event.setContentDescription(mView.getText()); + } + } + + @Override + protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo info) { + final ClickableSpan span = getSpanForOffset(virtualViewId); + if (span != null) { + info.setContentDescription(getTextForSpan(span)); + } else { + Log.e(TAG, "ClickableSpan is null for offset: " + virtualViewId); + info.setContentDescription(mView.getText()); + } + info.setFocusable(true); + info.setClickable(true); + getBoundsForSpan(span, mTempRect); + if (!mTempRect.isEmpty()) { + info.setBoundsInParent(getBoundsForSpan(span, mTempRect)); + } else { + Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId); + mTempRect.set(0, 0, 1, 1); + info.setBoundsInParent(mTempRect); + } + info.addAction(AccessibilityNodeInfo.ACTION_CLICK); + } + + @Override + protected boolean onPerformActionForVirtualView(int virtualViewId, int action, + Bundle arguments) { + if (action == AccessibilityNodeInfo.ACTION_CLICK) { + ClickableSpan span = getSpanForOffset(virtualViewId); + if (span != null) { + span.onClick(mView); + return true; + } else { + Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId); + } + } + return false; + } + + private ClickableSpan getSpanForOffset(int offset) { + CharSequence text = mView.getText(); + if (text instanceof Spanned) { + Spanned spannedText = (Spanned) text; + ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class); + if (spans.length == 1) { + return spans[0]; + } + } + return null; + } + + private CharSequence getTextForSpan(ClickableSpan span) { + CharSequence text = mView.getText(); + if (text instanceof Spanned) { + Spanned spannedText = (Spanned) text; + return spannedText.subSequence(spannedText.getSpanStart(span), + spannedText.getSpanEnd(span)); + } + return text; + } + + // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the + // section on the first line. + private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) { + CharSequence text = mView.getText(); + outRect.setEmpty(); + if (text instanceof Spanned) { + Spanned spannedText = (Spanned) text; + final int spanStart = spannedText.getSpanStart(span); + final int spanEnd = spannedText.getSpanEnd(span); + final Layout layout = mView.getLayout(); + final float xStart = layout.getPrimaryHorizontal(spanStart); + final float xEnd = layout.getPrimaryHorizontal(spanEnd); + final int lineStart = layout.getLineForOffset(spanStart); + final int lineEnd = layout.getLineForOffset(spanEnd); + layout.getLineBounds(lineStart, outRect); + outRect.left = (int) xStart; + if (lineEnd == lineStart) { + outRect.right = (int) xEnd; + } // otherwise just leave it at the end of the start line + + // Offset for padding + outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop()); + } + return outRect; + } +} |