summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaurice Lam <yukl@google.com>2018-03-05 15:59:43 -0800
committerMaurice Lam <yukl@google.com>2018-04-04 19:51:32 -0700
commit618a4449bb3e9be43586040ea2fb9a6371365ae7 (patch)
tree38bb10565cd00959f7cdc5649a30cf541407e4da
parent1643b5132fbbbc376279785a512d93adc7a46cd7 (diff)
downloadsetupwizard-618a4449bb3e9be43586040ea2fb9a6371365ae7.tar.gz
Add touch feedback to links
- Clear selection after LinkSpan is clicked so that the highlight effect will be cleared when the tap completes - Set focusableInTouchMode to true and revealOnFocusHint to false in RichTextView for N MR1 or above to allow the highlight effect to be visible in touch mode. Test: ./gradlew test connectedAndroidTest Bug: 73350031 Change-Id: Ibb6f67102775802cdfebaa1529c09d936b4096cb (cherry picked from commit bc1c7a159c14f8b8f532fec60681d34771cd7909)
-rw-r--r--library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java11
-rw-r--r--library/main/src/com/android/setupwizardlib/span/LinkSpan.java11
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java32
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java (renamed from library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java)36
4 files changed, 73 insertions, 17 deletions
diff --git a/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
index e6bc9da..6694cb2 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
@@ -130,6 +130,17 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
// as individual TextViews consume touch events and thereby reducing the focus window
// shown by Talkback. Disable focus if there are no links
setFocusable(hasLinks);
+ // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
+ // focusable in touch mode, we may be focused when the screen is first shown, and starting
+ // a screen halfway scrolled down is confusing to the user.
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ setRevealOnFocusHint(false);
+ // setRevealOnFocusHint is a new API added in SDK 25. For lower SDK versions, do not
+ // call setFocusableInTouchMode. We won't get touch effect on those earlier versions,
+ // but the link will still work, and will prevent the scroll view from starting halfway
+ // down the page.
+ setFocusableInTouchMode(hasLinks);
+ }
}
private boolean hasLinks(CharSequence text) {
diff --git a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
index a5f0424..26a3d16 100644
--- a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
+++ b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
@@ -21,10 +21,13 @@ import android.content.ContextWrapper;
import android.graphics.Typeface;
import android.os.Build;
import android.support.annotation.Nullable;
+import android.text.Selection;
+import android.text.Spannable;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.View;
+import android.widget.TextView;
/**
* A clickable span that will listen for click events and send it back to the context. To use this
@@ -86,11 +89,19 @@ public class LinkSpan extends ClickableSpan {
public void onClick(View view) {
if (dispatchClick(view)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ // Prevent the touch event from bubbling up to the parent views.
view.cancelPendingInputEvents();
}
} else {
Log.w(TAG, "Dropping click event. No listener attached.");
}
+ if (view instanceof TextView) {
+ // Remove the highlight effect when the click happens by clearing the selection
+ CharSequence text = ((TextView) view).getText();
+ if (text instanceof Spannable) {
+ Selection.setSelection((Spannable) text, 0);
+ }
+ }
}
private boolean dispatchClick(View view) {
diff --git a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
index fe72e03..3aafa7d 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
@@ -16,11 +16,16 @@
package com.android.setupwizardlib.span;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertSame;
import static org.robolectric.RuntimeEnvironment.application;
import android.content.Context;
import android.content.ContextWrapper;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
@@ -32,7 +37,7 @@ import org.junit.runner.RunWith;
public class LinkSpanTest {
@Test
- public void testOnClick() {
+ public void onClick_shouldCallListenerOnContext() {
final TestContext context = new TestContext(application);
final TextView textView = new TextView(context);
final LinkSpan linkSpan = new LinkSpan("test_id");
@@ -43,7 +48,7 @@ public class LinkSpanTest {
}
@Test
- public void testNonImplementingContext() {
+ public void onClick_contextDoesNotImplementOnClickListener_shouldBeNoOp() {
final TextView textView = new TextView(application);
final LinkSpan linkSpan = new LinkSpan("test_id");
@@ -54,7 +59,7 @@ public class LinkSpanTest {
}
@Test
- public void testWrappedListener() {
+ public void onClick_contextWrapsOnClickListener_shouldCallWrappedListener() {
final TestContext context = new TestContext(application);
final Context wrapperContext = new ContextWrapper(context);
final TextView textView = new TextView(wrapperContext);
@@ -65,6 +70,27 @@ public class LinkSpanTest {
assertSame("Clicked LinkSpan should be passed to setup", linkSpan, context.clickedSpan);
}
+ @Test
+ public void onClick_shouldClearSelection() {
+ final TestContext context = new TestContext(application);
+ final TextView textView = new TextView(context);
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ textView.setFocusable(true);
+ textView.setFocusableInTouchMode(true);
+ final LinkSpan linkSpan = new LinkSpan("test_id");
+
+ SpannableStringBuilder text = new SpannableStringBuilder("Lorem ipsum dolor sit");
+ textView.setText(text);
+ text.setSpan(linkSpan, /* start= */ 0, /* end= */ 5, /* flags= */ 0);
+ // Simulate the touch effect set by TextView when touched.
+ Selection.setSelection(text, /* start= */ 0, /* end= */ 5);
+
+ linkSpan.onClick(textView);
+
+ assertThat(Selection.getSelectionStart(textView.getText())).isEqualTo(0);
+ assertThat(Selection.getSelectionEnd(textView.getText())).isEqualTo(0);
+ }
+
@SuppressWarnings("deprecation")
private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
index 5f3eb9f..2e28b48 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.setupwizardlib.test;
+package com.android.setupwizardlib.view;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -24,29 +24,30 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.ContextWrapper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.text.Annotation;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TextAppearanceSpan;
+import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import com.android.setupwizardlib.span.LinkSpan;
import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
-import com.android.setupwizardlib.view.RichTextView;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
import java.util.Arrays;
-@RunWith(AndroidJUnit4.class)
-@SmallTest
+@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class RichTextViewTest {
@Test
@@ -55,7 +56,7 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText(ssb);
final CharSequence text = textView.getText();
@@ -77,7 +78,7 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText(ssb);
OnLinkClickListener listener = mock(OnLinkClickListener.class);
@@ -99,7 +100,7 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- TestContext context = spy(new TestContext(InstrumentationRegistry.getTargetContext()));
+ TestContext context = spy(new TestContext(application));
RichTextView textView = new RichTextView(context);
textView.setText(ssb);
@@ -116,7 +117,7 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText(ssb);
final CharSequence text = textView.getText();
@@ -137,7 +138,7 @@ public class RichTextViewTest {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked");
spannableStringBuilder.setSpan(testLink, 0, 3, 0);
- RichTextView view = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView view = new RichTextView(application);
view.setText(spannableStringBuilder);
assertTrue("TextView should be focusable since it contains spans", view.isFocusable());
@@ -147,7 +148,7 @@ public class RichTextViewTest {
@SuppressLint("SetTextI18n") // It's OK. This is just a test.
@Test
public void testTextContainingNoLinksAreNotFocusable() {
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText("Thou shall not be focusable!");
assertFalse("TextView should not be focusable since it does not contain any span",
@@ -160,16 +161,23 @@ public class RichTextViewTest {
@SuppressLint("SetTextI18n") // It's OK. This is just a test.
@Test
public void testRichTextViewFocusChangesWithTextChange() {
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText("Thou shall not be focusable!");
assertFalse(textView.isFocusable());
+ assertFalse(textView.isFocusableInTouchMode());
SpannableStringBuilder spannableStringBuilder =
new SpannableStringBuilder("I am focusable");
spannableStringBuilder.setSpan(new Annotation("link", "focus:on_me"), 0, 1, 0);
textView.setText(spannableStringBuilder);
assertTrue(textView.isFocusable());
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ assertTrue(textView.isFocusableInTouchMode());
+ assertFalse(textView.getRevealOnFocusHint());
+ } else {
+ assertFalse(textView.isFocusableInTouchMode());
+ }
}
public static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {