diff --git a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java index e6fa497..1bfd4c7 100644 --- a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java +++ b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java @@ -17,13 +17,19 @@ package com.android.setupwizardlib.util; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; +import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; import android.support.v4.widget.ExploreByTouchHelper; import android.text.Layout; import android.text.Spanned; import android.text.style.ClickableSpan; import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; @@ -32,6 +38,10 @@ import java.util.List; /** * An accessibility delegate that allows {@link android.text.style.ClickableSpan} to be focused and * clicked by accessibility services. + *
+ * Note: From Android O on, there is native support for ClickableSpan + * accessibility, so this class is not needed (and indeed has no effect.) + *
* * Sample usage: *@@ -54,19 +64,135 @@ import java.util.List; * @see com.android.setupwizardlib.view.RichTextView * @see android.support.v4.widget.ExploreByTouchHelper */ -public class LinkAccessibilityHelper extends ExploreByTouchHelper { +public class LinkAccessibilityHelper extends AccessibilityDelegateCompat { private static final String TAG = "LinkAccessibilityHelper"; private final TextView mView; private final Rect mTempRect = new Rect(); + private final ExploreByTouchHelper mExploreByTouchHelper; public LinkAccessibilityHelper(TextView view) { - super(view); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) { + // Pre-O, we essentially extend ExploreByTouchHelper to expose a virtual view hierarchy + mExploreByTouchHelper = new ExploreByTouchHelper(view) { + @Override + protected int getVirtualViewAt(float x, float y) { + return this.getVirtualViewAt(x, y); + } + + @Override + protected void getVisibleVirtualViews(ListvirtualViewIds) { + this.getVisibleVirtualViews(virtualViewIds); + } + + @Override + protected void onPopulateEventForVirtualView(int virtualViewId, + AccessibilityEvent event) { + this.onPopulateEventForVirtualView(virtualViewId, event); + } + + @Override + protected void onPopulateNodeForVirtualView(int virtualViewId, + AccessibilityNodeInfoCompat infoCompat) { + this.onPopulateNodeForVirtualView(virtualViewId, infoCompat); + + } + + @Override + protected boolean onPerformActionForVirtualView(int virtualViewId, int action, + Bundle arguments) { + return this.onPerformActionForVirtualView(virtualViewId, action, arguments); + } + }; + } else { + mExploreByTouchHelper = null; + } mView = view; } @Override + public void sendAccessibilityEvent(View host, int eventType) { + if (mExploreByTouchHelper != null) { + mExploreByTouchHelper.sendAccessibilityEvent(host, eventType); + } else { + super.sendAccessibilityEvent(host, eventType); + } + } + + @Override + public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { + if (mExploreByTouchHelper != null) { + mExploreByTouchHelper.sendAccessibilityEventUnchecked(host, event); + } else { + super.sendAccessibilityEventUnchecked(host, event); + } + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + return (mExploreByTouchHelper != null) + ? mExploreByTouchHelper.dispatchPopulateAccessibilityEvent(host, event) + : super.dispatchPopulateAccessibilityEvent(host, event); + } + + @Override + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + if (mExploreByTouchHelper != null) { + mExploreByTouchHelper.onPopulateAccessibilityEvent(host, event); + } else { + super.onPopulateAccessibilityEvent(host, event); + } + } + + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + if (mExploreByTouchHelper != null) { + mExploreByTouchHelper.onInitializeAccessibilityEvent(host, event); + } else { + super.onInitializeAccessibilityEvent(host, event); + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + if (mExploreByTouchHelper != null) { + mExploreByTouchHelper.onInitializeAccessibilityNodeInfo(host, info); + } else { + super.onInitializeAccessibilityNodeInfo(host, info); + } + } + + @Override + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + return (mExploreByTouchHelper != null) + ? mExploreByTouchHelper.onRequestSendAccessibilityEvent(host, child, event) + : super.onRequestSendAccessibilityEvent(host, child, event); + } + + @Override + public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) { + return (mExploreByTouchHelper != null) + ? mExploreByTouchHelper.getAccessibilityNodeProvider(host) + : super.getAccessibilityNodeProvider(host); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + return (mExploreByTouchHelper != null) + ? mExploreByTouchHelper.performAccessibilityAction(host, action, args) + : super.performAccessibilityAction(host, action, args); + } + + /** + * Delegated to {@link ExploreByTouchHelper} + */ + public final boolean dispatchHoverEvent(MotionEvent event) { + return (mExploreByTouchHelper != null) ? mExploreByTouchHelper.dispatchHoverEvent(event) + : false; + } + protected int getVirtualViewAt(float x, float y) { final CharSequence text = mView.getText(); if (text instanceof Spanned) { @@ -78,10 +204,9 @@ public class LinkAccessibilityHelper extends ExploreByTouchHelper { return spannedText.getSpanStart(linkSpan); } } - return INVALID_ID; + return ExploreByTouchHelper.INVALID_ID; } - @Override protected void getVisibleVirtualViews(List virtualViewIds) { final CharSequence text = mView.getText(); if (text instanceof Spanned) { @@ -94,7 +219,6 @@ public class LinkAccessibilityHelper extends ExploreByTouchHelper { } } - @Override protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { final ClickableSpan span = getSpanForOffset(virtualViewId); if (span != null) { @@ -105,7 +229,6 @@ public class LinkAccessibilityHelper extends ExploreByTouchHelper { } } - @Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfoCompat info) { final ClickableSpan span = getSpanForOffset(virtualViewId); @@ -126,7 +249,6 @@ public class LinkAccessibilityHelper extends ExploreByTouchHelper { info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); } - @Override protected boolean onPerformActionForVirtualView(int virtualViewId, int action, Bundle arguments) { if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { diff --git a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java index a1d01fd..844e73e 100644 --- a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java +++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -57,6 +58,7 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVirtualViewAt() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10)); assertEquals("Virtual view ID should be 1", 1, virtualViewId); @@ -64,6 +66,7 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVirtualViewAtHost() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100)); assertEquals("Virtual view ID should be INVALID_ID", @@ -72,6 +75,7 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVisibleVirtualViews() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); List virtualViewIds = new ArrayList<>(); mHelper.getVisibleVirtualViews(virtualViewIds); @@ -82,6 +86,7 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateEventForVirtualView() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityEvent event = AccessibilityEvent.obtain(); mHelper.onPopulateEventForVirtualView(1, event); @@ -95,6 +100,7 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateEventForVirtualViewHost() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityEvent event = AccessibilityEvent.obtain(); mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event); @@ -107,6 +113,7 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateNodeForVirtualView() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); mHelper.onPopulateNodeForVirtualView(1, info); @@ -125,6 +132,7 @@ public class LinkAccessibilityHelperTest { @Test public void testNullLayout() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); // Setting the padding will cause the layout to be null-ed out. mTextView.setPadding(1, 1, 1, 1); @@ -142,6 +150,7 @@ public class LinkAccessibilityHelperTest { @Test public void testRtlLayout() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום"); ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */); initTextView(ssb); @@ -161,6 +170,7 @@ public class LinkAccessibilityHelperTest { @Test public void testMultilineLink() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; SpannableStringBuilder ssb = new SpannableStringBuilder( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Praesent accumsan efficitur eros eu porttitor."); @@ -182,6 +192,7 @@ public class LinkAccessibilityHelperTest { @Test public void testRtlMultilineLink() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים " + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד " + "דפים המחשב מיזמים ב."; @@ -205,6 +216,7 @@ public class LinkAccessibilityHelperTest { @Test public void testBidiMultilineLink() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים " + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד " + "דפים המחשב מיזמים ב.";