1d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam/*
2d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * Copyright (C) 2016 The Android Open Source Project
3d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam *
4d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * Licensed under the Apache License, Version 2.0 (the "License");
5d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * you may not use this file except in compliance with the License.
6d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * You may obtain a copy of the License at
7d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam *
8d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam *      http://www.apache.org/licenses/LICENSE-2.0
9d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam *
10d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * Unless required by applicable law or agreed to in writing, software
11d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * distributed under the License is distributed on an "AS IS" BASIS,
12d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * See the License for the specific language governing permissions and
14d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam * limitations under the License.
15d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam */
16d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
17b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lampackage com.android.setupwizardlib.util;
18d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
192614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport static org.junit.Assert.assertEquals;
202614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport static org.junit.Assert.assertTrue;
21b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport static org.mockito.Matchers.eq;
22b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport static org.mockito.Matchers.same;
23b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport static org.mockito.Mockito.mock;
24b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport static org.mockito.Mockito.verify;
252614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam
26d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.graphics.Rect;
27d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.os.Bundle;
282614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport android.support.test.InstrumentationRegistry;
292614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport android.support.test.filters.SmallTest;
302614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport android.support.test.runner.AndroidJUnit4;
317c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lamimport android.support.v4.text.BidiFormatter;
32d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
33b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
34d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.support.v4.widget.ExploreByTouchHelper;
35d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.text.SpannableStringBuilder;
36059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lamimport android.util.DisplayMetrics;
37059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lamimport android.util.TypedValue;
38b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport android.view.MotionEvent;
397c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lamimport android.view.View;
40d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.view.accessibility.AccessibilityEvent;
41b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport android.widget.FrameLayout;
42d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport android.widget.TextView;
43d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
44d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport com.android.setupwizardlib.span.LinkSpan;
45b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lamimport com.android.setupwizardlib.util.LinkAccessibilityHelper.PreOLinkAccessibilityHelper;
46d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
472614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport org.junit.Test;
482614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lamimport org.junit.runner.RunWith;
492614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam
50d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport java.util.ArrayList;
51d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport java.util.Collections;
52d832154e333a3a45b5faecd518b664ddd297183cMaurice Lamimport java.util.List;
53d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
542614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam@RunWith(AndroidJUnit4.class)
552614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam@SmallTest
562614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lampublic class LinkAccessibilityHelperTest {
57d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
587c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    private static final LinkSpan LINK_SPAN = new LinkSpan("foobar");
597c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
60d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    private TextView mTextView;
61b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam    private TestPreOLinkAccessibilityHelper mHelper;
62d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
63059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam    private DisplayMetrics mDisplayMetrics;
64059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam
652614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
66d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    public void testGetVirtualViewAt() {
677c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
68059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam        final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10));
69d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertEquals("Virtual view ID should be 1", 1, virtualViewId);
70d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
71d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
722614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
73d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    public void testGetVirtualViewAtHost() {
747c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
75059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam        final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100));
76d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertEquals("Virtual view ID should be INVALID_ID",
77d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                ExploreByTouchHelper.INVALID_ID, virtualViewId);
78d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
79d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
802614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
81d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    public void testGetVisibleVirtualViews() {
827c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
83d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        List<Integer> virtualViewIds = new ArrayList<>();
84d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        mHelper.getVisibleVirtualViews(virtualViewIds);
85d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
86d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertEquals("VisibleVirtualViews should be [1]",
87d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                Collections.singletonList(1), virtualViewIds);
88d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
89d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
902614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
91d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    public void testOnPopulateEventForVirtualView() {
927c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
93d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        AccessibilityEvent event = AccessibilityEvent.obtain();
94d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        mHelper.onPopulateEventForVirtualView(1, event);
95d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
96d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        // LinkSpan is set on substring(1, 2) of "Hello world" --> "e"
97d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertEquals("LinkSpan description should be \"e\"",
98d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                "e", event.getContentDescription().toString());
99d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
100d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        event.recycle();
101d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
102d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
1032614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
104d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    public void testOnPopulateEventForVirtualViewHost() {
1057c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
106d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        AccessibilityEvent event = AccessibilityEvent.obtain();
107d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
108d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
109d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertEquals("Host view description should be \"Hello world\"", "Hello world",
110d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                event.getContentDescription().toString());
111d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
112d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        event.recycle();
113d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
114d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
1152614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
116d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    public void testOnPopulateNodeForVirtualView() {
1177c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
118d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
119d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        mHelper.onPopulateNodeForVirtualView(1, info);
120d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
121d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertEquals("LinkSpan description should be \"e\"",
122d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                "e", info.getContentDescription().toString());
123d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertTrue("LinkSpan should be focusable", info.isFocusable());
124d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        assertTrue("LinkSpan should be clickable", info.isClickable());
125d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        Rect bounds = new Rect();
126d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        info.getBoundsInParent(bounds);
127059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam        assertEquals("LinkSpan bounds should be (10.5dp, 0dp, 18.5dp, 20.5dp)",
128059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam                new Rect(dp2Px(10.5f), dp2Px(0f), dp2Px(18.5f), dp2Px(20.5f)), bounds);
129d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
130d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        info.recycle();
131d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
132d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
1332614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
134cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam    public void testNullLayout() {
1357c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView();
136cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        // Setting the padding will cause the layout to be null-ed out.
137cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        mTextView.setPadding(1, 1, 1, 1);
138cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam
139cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
140cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        mHelper.onPopulateNodeForVirtualView(0, info);
141cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam
142cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        Rect bounds = new Rect();
143cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        info.getBoundsInParent(bounds);
144cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        assertEquals("LinkSpan bounds should be (0, 0, 1, 1)",
145cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam                new Rect(0, 0, 1, 1), bounds);
146cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam
147cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam        info.recycle();
148cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam    }
149cf90658b5c16018c9f3db7fd1d872025cff5d1dcMaurice Lam
1502614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
15104eecdd42f0bc9751825f9f21131a59852256278Maurice Lam    public void testRtlLayout() {
15204eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום");
1537c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
1547c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView(ssb);
15504eecdd42f0bc9751825f9f21131a59852256278Maurice Lam
15604eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
15704eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        mHelper.onPopulateNodeForVirtualView(1, info);
15804eecdd42f0bc9751825f9f21131a59852256278Maurice Lam
15904eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        assertEquals("LinkSpan description should be \"כ\"",
16004eecdd42f0bc9751825f9f21131a59852256278Maurice Lam                "כ", info.getContentDescription().toString());
16104eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        Rect bounds = new Rect();
16204eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        info.getBoundsInParent(bounds);
1637c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan bounds should be (481.5dp, 0dp, 489.5dp, 20.5dp)",
1647c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                new Rect(dp2Px(481.5f), dp2Px(0f), dp2Px(489.5f), dp2Px(20.5f)), bounds);
1657c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1667c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        info.recycle();
1677c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    }
1687c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1692614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
1707c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    public void testMultilineLink() {
1717c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        SpannableStringBuilder ssb = new SpannableStringBuilder(
1727c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
1737c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                + "Praesent accumsan efficitur eros eu porttitor.");
1747c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        ssb.setSpan(LINK_SPAN, 51, 74, 0 /* flags */);
1757c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView(ssb);
1767c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1777c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
1787c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mHelper.onPopulateNodeForVirtualView(51, info);
1797c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1807c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan description should match the span",
1817c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                "elit. Praesent accumsan", info.getContentDescription().toString());
1827c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        Rect bounds = new Rect();
1837c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        info.getBoundsInParent(bounds);
1847c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan bounds should match first line of the span",
1857c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                new Rect(dp2Px(343f), dp2Px(0f), dp2Px(500f), dp2Px(19.5f)), bounds);
1867c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1877c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        info.recycle();
1887c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    }
1897c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1902614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
1917c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    public void testRtlMultilineLink() {
1927c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
1937c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
1947c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                + "דפים המחשב מיזמים ב.";
1957c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        SpannableStringBuilder ssb = new SpannableStringBuilder(iwLoremIpsum);
1967c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        ssb.setSpan(LINK_SPAN, 50, 100, 0 /* flags */);
1977c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView(ssb);
1987c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
1997c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
2007c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mHelper.onPopulateNodeForVirtualView(50, info);
2017c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
2027c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan description should match the span",
2037c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                iwLoremIpsum.substring(50, 100),
2047c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                info.getContentDescription().toString());
2057c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        Rect bounds = new Rect();
2067c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        info.getBoundsInParent(bounds);
2077c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan bounds should match the first line of the span",
2087c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                new Rect(dp2Px(0f), dp2Px(0f), dp2Px(150f), dp2Px(19.5f)), bounds);
20904eecdd42f0bc9751825f9f21131a59852256278Maurice Lam
21004eecdd42f0bc9751825f9f21131a59852256278Maurice Lam        info.recycle();
21104eecdd42f0bc9751825f9f21131a59852256278Maurice Lam    }
21204eecdd42f0bc9751825f9f21131a59852256278Maurice Lam
2132614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam    @Test
2147c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    public void testBidiMultilineLink() {
2157c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
2167c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
2177c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                + "דפים המחשב מיזמים ב.";
2187c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        BidiFormatter formatter = BidiFormatter.getInstance(false /* rtlContext */);
2197c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        SpannableStringBuilder ssb = new SpannableStringBuilder();
2207c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        ssb.append("hello ").append(formatter.unicodeWrap(iwLoremIpsum)).append(" world");
2217c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        ssb.setSpan(LINK_SPAN,
2227c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                "hello ".length() + 2, // Add two for the characters added by BidiFormatter
2237c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                "hello ".length() + 2 + iwLoremIpsum.length(),
2247c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                0 /* flags */);
2257c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView(ssb);
2267c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
2277c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
2287c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mHelper.onPopulateNodeForVirtualView("hello ".length() + 2, info);
2297c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
2307c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan description should match the span",
2317c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                iwLoremIpsum,
2327c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                info.getContentDescription().toString());
2337c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        Rect bounds = new Rect();
2347c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        info.getBoundsInParent(bounds);
2357c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        assertEquals("LinkSpan bounds should match the first line of the span",
2367c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                new Rect(dp2Px(491.5f), dp2Px(0f), dp2Px(500f), dp2Px(19.5f)), bounds);
2377c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
2387c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        info.recycle();
2397c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    }
2407c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
241b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam    @Test
242b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam    public void testMethodDelegation() {
243b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        initTextView();
244b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class);
245b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
246b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
247b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        AccessibilityEvent accessibilityEvent =
248b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
249b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
250b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED);
251b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).sendAccessibilityEvent(
252b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
253b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                eq(AccessibilityEvent.TYPE_VIEW_CLICKED));
254b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
255b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent);
256b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent));
257b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
258b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.performAccessibilityAction(
259b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                mTextView,
260b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                AccessibilityActionCompat.ACTION_CLICK.getId(),
261b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                Bundle.EMPTY);
262b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).performAccessibilityAction(
263b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
264b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                eq(AccessibilityActionCompat.ACTION_CLICK.getId()),
265b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                eq(Bundle.EMPTY));
266b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
267b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.dispatchPopulateAccessibilityEvent(
268b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                mTextView,
269b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                accessibilityEvent);
270b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).dispatchPopulateAccessibilityEvent(
271b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
272b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(accessibilityEvent));
273b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
274b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0);
275b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.dispatchHoverEvent(motionEvent);
276b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).dispatchHoverEvent(eq(motionEvent));
277b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
278b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.getAccessibilityNodeProvider(mTextView);
279b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).getAccessibilityNodeProvider(same(mTextView));
280b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
281b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent);
282b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).onInitializeAccessibilityEvent(
283b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
284b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                eq(accessibilityEvent));
285b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
286b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain();
287b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo);
288b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).onInitializeAccessibilityNodeInfo(
289b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
290b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(accessibilityNodeInfo));
291b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
292b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent);
293b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).onPopulateAccessibilityEvent(
294b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
295b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(accessibilityEvent));
296b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
297b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext());
298b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent);
299b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        verify(delegate).onRequestSendAccessibilityEvent(
300b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(parent),
301b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(mTextView),
302b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam                same(accessibilityEvent));
303b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam    }
304b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam
3057c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    private void initTextView() {
3067c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
3077c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
3087c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        initTextView(ssb);
3097c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    }
3107c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
3117c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    private void initTextView(CharSequence text) {
3122614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam        mTextView = new TextView(InstrumentationRegistry.getContext());
3137c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mTextView.setSingleLine(false);
3147c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mTextView.setText(text);
3157c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
316b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        mHelper = new TestPreOLinkAccessibilityHelper(mTextView);
3177c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
3187c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        int measureExactly500dp = View.MeasureSpec.makeMeasureSpec(dp2Px(500),
3197c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam                View.MeasureSpec.EXACTLY);
3207c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mTextView.measure(measureExactly500dp, measureExactly500dp);
3217c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam        mTextView.layout(dp2Px(0), dp2Px(0), dp2Px(500), dp2Px(500));
3227c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam    }
3237c7aa7d0d3cd465b1f4c0aa812fc4006cd7a14adMaurice Lam
324059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam    private int dp2Px(float dp) {
325059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam        if (mDisplayMetrics == null) {
3262614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam            mDisplayMetrics =
3272614260933341bd4ea7bb37a3435f46f82cacef7Maurice Lam                    InstrumentationRegistry.getContext().getResources().getDisplayMetrics();
328059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam        }
329059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
330059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam    }
331059321ff28f79a2cbe630b4d814a96ff9254643aMaurice Lam
332b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam    public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper {
333d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
334b72f3fb4598d2bd2560cdf5043defc80a0199e2eMaurice Lam        TestPreOLinkAccessibilityHelper(TextView view) {
335d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam            super(view);
336d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        }
337d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
338d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        @Override
339d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        public int getVirtualViewAt(float x, float y) {
340d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam            return super.getVirtualViewAt(x, y);
341d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        }
342d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
343d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        @Override
344d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        public void getVisibleVirtualViews(List<Integer> virtualViewIds) {
345d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam            super.getVisibleVirtualViews(virtualViewIds);
346d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        }
347d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
348d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        @Override
349d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        public void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
350d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam            super.onPopulateEventForVirtualView(virtualViewId, event);
351d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        }
352d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
353d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        @Override
354d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        public void onPopulateNodeForVirtualView(int virtualViewId,
355d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                AccessibilityNodeInfoCompat info) {
356d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam            super.onPopulateNodeForVirtualView(virtualViewId, info);
357d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        }
358d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam
359d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        @Override
360d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        public boolean onPerformActionForVirtualView(int virtualViewId, int action,
361d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam                Bundle arguments) {
362d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam            return super.onPerformActionForVirtualView(virtualViewId, action, arguments);
363d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam        }
364d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam    }
365d832154e333a3a45b5faecd518b664ddd297183cMaurice Lam}
366