190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki/* 290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * Copyright (C) 2015 The Android Open Source Project 390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * 490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * Licensed under the Apache License, Version 2.0 (the "License"); 590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * you may not use this file except in compliance with the License. 690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * You may obtain a copy of the License at 790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * 890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * http://www.apache.org/licenses/LICENSE-2.0 990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * 1090cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * Unless required by applicable law or agreed to in writing, software 1190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * distributed under the License is distributed on an "AS IS" BASIS, 1290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * See the License for the specific language governing permissions and 1490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * limitations under the License 1590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki */ 1690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 1790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokipackage android.widget.espresso; 1890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 1990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport static android.support.test.espresso.action.ViewActions.actionWithAssertions; 206b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir 21da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagiimport android.graphics.Rect; 2290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.support.test.espresso.PerformException; 2390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.support.test.espresso.ViewAction; 2490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.support.test.espresso.action.CoordinatesProvider; 256b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinirimport android.support.test.espresso.action.GeneralLocation; 2690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.support.test.espresso.action.Press; 2790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.support.test.espresso.action.Tap; 2890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.support.test.espresso.util.HumanReadables; 2990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.text.Layout; 30a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagiimport android.view.MotionEvent; 3190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.view.View; 32da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagiimport android.widget.Editor; 33f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagiimport android.widget.Editor.HandleView; 3490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokiimport android.widget.TextView; 3590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 3690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki/** 3790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * A collection of actions on a {@link android.widget.TextView}. 3890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki */ 3990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Tokipublic final class TextViewActions { 4090cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 4190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki private TextViewActions() {} 4290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 4390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki /** 44ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * Returns an action that clicks on text at an index on the TextView.<br> 4590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * <br> 4690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * View constraints: 4790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * <ul> 48ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <li>must be a TextView displayed on screen 4990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * <ul> 50ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * 51ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * @param index The index of the TextView's text to click on. 5290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki */ 5390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki public static ViewAction clickOnTextAtIndex(int index) { 5490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki return actionWithAssertions( 555f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi new ViewClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER)); 5690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } 5790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 586b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir 596b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir /** 606b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir * Returns an action that single-clicks by mouse on the View.<br> 616b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir * <br> 626b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir * View constraints: 636b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir * <ul> 646b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir * <li>must be a View displayed on screen 656b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir * <ul> 666b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir */ 676b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir public static ViewAction mouseClick() { 686b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir return actionWithAssertions(new MouseClickAction(Tap.SINGLE, GeneralLocation.VISIBLE_CENTER, 696b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir MotionEvent.BUTTON_PRIMARY)); 706b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir } 716b61d816ddbdd251ce48d177c6cffe3dda507877Siyamed Sinir 7290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki /** 732ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Returns an action that clicks by mouse on text at an index on the TextView.<br> 742ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <br> 752ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * View constraints: 762ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 772ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 782ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 792ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 802ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param index The index of the TextView's text to click on. 812ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 822ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public static ViewAction mouseClickOnTextAtIndex(int index) { 83a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi return mouseClickOnTextAtIndex(index, MotionEvent.BUTTON_PRIMARY); 84a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi } 85a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi 86a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi /** 87a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * Returns an action that clicks by mouse on text at an index on the TextView.<br> 88a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * <br> 89a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * View constraints: 90a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * <ul> 91a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 92a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * <ul> 93a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * 94a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * @param index The index of the TextView's text to click on. 95a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi * @param button the mouse button to use. 96a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi */ 97a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi public static ViewAction mouseClickOnTextAtIndex(int index, 98a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi @MouseUiController.MouseButton int button) { 992ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi return actionWithAssertions( 100a0b3c068810b2bc4ed61d9ad35e9660aa247d2f6Keisuke Kuroyanagi new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), button)); 1012ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1022ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 1032ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi /** 1048a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * Returns an action that double-clicks on text at an index on the TextView.<br> 1058a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <br> 1068a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * View constraints: 1078a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <ul> 1088a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <li>must be a TextView displayed on screen 1098a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <ul> 1108a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * 1118a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * @param index The index of the TextView's text to double-click on. 1128a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki */ 1138a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki public static ViewAction doubleClickOnTextAtIndex(int index) { 1148a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki return actionWithAssertions( 1155f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi new ViewClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER)); 1168a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki } 1178a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki 1188a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki /** 1192ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Returns an action that double-clicks by mouse on text at an index on the TextView.<br> 1202ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <br> 1212ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * View constraints: 1222ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 1232ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 1242ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 1252ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 1262ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param index The index of the TextView's text to double-click on. 1272ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 1282ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public static ViewAction mouseDoubleClickOnTextAtIndex(int index) { 1292ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi return actionWithAssertions( 13046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index))); 1312ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1322ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 1332ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi /** 1348a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * Returns an action that long presses on text at an index on the TextView.<br> 1358a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <br> 1368a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * View constraints: 1378a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <ul> 1388a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <li>must be a TextView displayed on screen 1398a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * <ul> 1408a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * 1418a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki * @param index The index of the TextView's text to long press on. 1428a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki */ 1438a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki public static ViewAction longPressOnTextAtIndex(int index) { 1448a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki return actionWithAssertions( 1455f318b62853495f286e011de9cb1571e22431314Keisuke Kuroyanagi new ViewClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER)); 1468a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki } 1478a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki 1488a5e1ae2f4e1aaf2db2a217e841371e18851df3fAbodunrinwa Toki /** 1492ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Returns an action that long click by mouse on text at an index on the TextView.<br> 1502ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <br> 1512ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * View constraints: 1522ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 1532ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 1542ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 1552ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 1562ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param index The index of the TextView's text to long click on. 1572ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 1582ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public static ViewAction mouseLongClickOnTextAtIndex(int index) { 1592ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi return actionWithAssertions( 16046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi new MouseClickAction(Tap.LONG, new TextCoordinates(index))); 16146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 16246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi 16346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi /** 16446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * Returns an action that triple-clicks by mouse on text at an index on the TextView.<br> 16546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <br> 16646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * View constraints: 16746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <ul> 16846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 16946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <ul> 17046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * 17146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * @param index The index of the TextView's text to triple-click on. 17246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi */ 17346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi public static ViewAction mouseTripleClickOnTextAtIndex(int index) { 17446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return actionWithAssertions( 17546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi new MouseClickAction(MouseClickAction.CLICK.TRIPLE, new TextCoordinates(index))); 1762ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 1772ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 1782ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi /** 179ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * Returns an action that long presses then drags on text from startIndex to endIndex on the 180ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * TextView.<br> 181ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <br> 182ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * View constraints: 183ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <ul> 184ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <li>must be a TextView displayed on screen 185ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <ul> 186ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * 187ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * @param startIndex The index of the TextView's text to start a drag from 188ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * @param endIndex The index of the TextView's text to end the drag at 189ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki */ 190ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki public static ViewAction longPressAndDragOnText(int startIndex, int endIndex) { 191ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki return actionWithAssertions( 192da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi new DragAction( 193da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi DragAction.Drag.LONG_PRESS, 194ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki new TextCoordinates(startIndex), 195ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki new TextCoordinates(endIndex), 196da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi Press.FINGER, 197da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi TextView.class)); 198ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki } 199ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki 200ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki /** 201ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * Returns an action that double taps then drags on text from startIndex to endIndex on the 202ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * TextView.<br> 203ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <br> 204ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * View constraints: 205ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <ul> 206ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <li>must be a TextView displayed on screen 207ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * <ul> 208ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * 209ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * @param startIndex The index of the TextView's text to start a drag from 210ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki * @param endIndex The index of the TextView's text to end the drag at 211ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki */ 212ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki public static ViewAction doubleTapAndDragOnText(int startIndex, int endIndex) { 213ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki return actionWithAssertions( 214da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi new DragAction( 215da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi DragAction.Drag.DOUBLE_TAP, 216ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki new TextCoordinates(startIndex), 217ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki new TextCoordinates(endIndex), 218da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi Press.FINGER, 219da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi TextView.class)); 220ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki } 221ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki 222ca4aaf3c17fdea4e373accc212867bd9f1e2be7fAbodunrinwa Toki /** 22388cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * Returns an action that click then drags by mouse on text from startIndex to endIndex on the 22488cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * TextView.<br> 22588cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * <br> 22688cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * View constraints: 22788cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * <ul> 22888cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 22988cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * <ul> 23088cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * 23188cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * @param startIndex The index of the TextView's text to start a drag from 23288cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi * @param endIndex The index of the TextView's text to end the drag at 23388cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi */ 23488cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi public static ViewAction mouseDragOnText(int startIndex, int endIndex) { 23588cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi return actionWithAssertions( 236da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi new DragAction( 237da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi DragAction.Drag.MOUSE_DOWN, 23888cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi new TextCoordinates(startIndex), 2392ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi new TextCoordinates(endIndex), 2402ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi Press.PINPOINT, 2412ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi TextView.class)); 2422ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 2432ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 2442ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi /** 2452ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Returns an action that double click then drags by mouse on text from startIndex to endIndex 2462ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * on the TextView.<br> 2472ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <br> 2482ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * View constraints: 2492ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 2502ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 2512ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 2522ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 2532ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param startIndex The index of the TextView's text to start a drag from 2542ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param endIndex The index of the TextView's text to end the drag at 2552ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 2562ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public static ViewAction mouseDoubleClickAndDragOnText(int startIndex, int endIndex) { 2572ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi return actionWithAssertions( 2582ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi new DragAction( 2592ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi DragAction.Drag.MOUSE_DOUBLE_CLICK, 2602ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi new TextCoordinates(startIndex), 2612ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi new TextCoordinates(endIndex), 2622ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi Press.PINPOINT, 2632ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi TextView.class)); 2642ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi } 2652ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi 2662ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi /** 2672ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * Returns an action that long click then drags by mouse on text from startIndex to endIndex 2682ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * on the TextView.<br> 2692ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <br> 2702ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * View constraints: 2712ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 2722ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 2732ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * <ul> 2742ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * 2752ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param startIndex The index of the TextView's text to start a drag from 2762ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi * @param endIndex The index of the TextView's text to end the drag at 2772ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi */ 2782ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi public static ViewAction mouseLongClickAndDragOnText(int startIndex, int endIndex) { 2792ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi return actionWithAssertions( 2802ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi new DragAction( 2812ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi DragAction.Drag.MOUSE_LONG_CLICK, 2822ff41d4afca7216cca4a224228caec2a5efaf278Keisuke Kuroyanagi new TextCoordinates(startIndex), 28388cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi new TextCoordinates(endIndex), 284da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi Press.PINPOINT, 285da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi TextView.class)); 286da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } 287da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi 28846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi /** 28946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * Returns an action that triple click then drags by mouse on text from startIndex to endIndex 29046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * on the TextView.<br> 29146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <br> 29246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * View constraints: 29346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <ul> 29446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <li>must be a TextView displayed on screen 29546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * <ul> 29646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * 29746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * @param startIndex The index of the TextView's text to start a drag from 29846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi * @param endIndex The index of the TextView's text to end the drag at 29946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi */ 30046faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi public static ViewAction mouseTripleClickAndDragOnText(int startIndex, int endIndex) { 30146faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi return actionWithAssertions( 30246faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi new DragAction( 30346faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi DragAction.Drag.MOUSE_TRIPLE_CLICK, 30446faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi new TextCoordinates(startIndex), 30546faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi new TextCoordinates(endIndex), 30646faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi Press.PINPOINT, 30746faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi TextView.class)); 30846faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi } 30946faad60230ade76b6a4944a2b9fae274698ab91Keisuke Kuroyanagi 310da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi public enum Handle { 311da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi SELECTION_START, 312da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi SELECTION_END, 313da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi INSERTION 314da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi }; 315da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi 316da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi /** 317da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * Returns an action that tap then drags on the handle from the current position to endIndex on 318da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * the TextView.<br> 319da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * <br> 320da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * View constraints: 321da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * <ul> 322da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * <li>must be a TextView's drag-handle displayed on screen 323da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * <ul> 324da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * 325da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * @param textView TextView the handle is on 326da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * @param handleType Type of the handle 327da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * @param endIndex The index of the TextView's text to end the drag at 328da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi */ 329da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex) { 330f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return dragHandle(textView, handleType, endIndex, true); 331f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 332f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 333f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi /** 334f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * Returns an action that tap then drags on the handle from the current position to endIndex on 335f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * the TextView.<br> 336f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * <br> 337f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * View constraints: 338f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * <ul> 339f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * <li>must be a TextView's drag-handle displayed on screen 340f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * <ul> 341f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * 342f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param textView TextView the handle is on 343f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param handleType Type of the handle 344f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param endIndex The index of the TextView's text to end the drag at 345f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param primary whether to use primary direction to get coordinate form index when endIndex is 346f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * at a direction boundary. 347f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi */ 348f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex, 349f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi boolean primary) { 350da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi return actionWithAssertions( 351da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi new DragAction( 352da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi DragAction.Drag.TAP, 353f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi new CurrentHandleCoordinates(textView), 3547557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader new HandleCoordinates(textView, handleType, endIndex, primary), 355da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi Press.FINGER, 356da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi Editor.HandleView.class)); 357da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } 3587557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader 359da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi /** 360f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * A provider of the x, y coordinates of the handle dragging point. 361f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi */ 362f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private static final class CurrentHandleCoordinates implements CoordinatesProvider { 363f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi // Must be larger than Editor#LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS. 364f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private final TextView mTextView; 365f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private final String mActionDescription; 366f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 367f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 368f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi public CurrentHandleCoordinates(TextView textView) { 369f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi mTextView = textView; 370f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi mActionDescription = "Could not locate handle."; 371f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 372f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 373f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi @Override 374f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi public float[] calculateCoordinates(View view) { 375f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi try { 376f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return locateHandle(view); 377f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } catch (StringIndexOutOfBoundsException e) { 378f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi throw new PerformException.Builder() 379f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withActionDescription(mActionDescription) 380f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withViewDescription(HumanReadables.describe(view)) 381f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withCause(e) 382f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .build(); 383f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 384f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 385f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 386f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private float[] locateHandle(View view) { 387f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final Rect bounds = new Rect(); 388f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi view.getBoundsOnScreen(bounds); 389f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final Rect visibleDisplayBounds = new Rect(); 390f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi mTextView.getWindowVisibleDisplayFrame(visibleDisplayBounds); 391f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi visibleDisplayBounds.right -= 1; 392f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi visibleDisplayBounds.bottom -= 1; 393f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi if (!visibleDisplayBounds.intersect(bounds)) { 394f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi throw new PerformException.Builder() 395f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withActionDescription(mActionDescription 396f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi + " The handle is entirely out of the visible display frame of" 397f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi + "the TextView's window.") 398f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withViewDescription(HumanReadables.describe(view)) 399f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .build(); 400f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 401f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final float dragPointX = Math.max(Math.min(bounds.centerX(), 402f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi visibleDisplayBounds.right), visibleDisplayBounds.left); 403f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final float verticalOffset = bounds.height() * 0.7f; 404f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final float dragPointY = Math.max(Math.min(bounds.top + verticalOffset, 405f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi visibleDisplayBounds.bottom), visibleDisplayBounds.top); 406f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return new float[] {dragPointX, dragPointY}; 407f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 408f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 409f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 410f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi /** 411da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * A provider of the x, y coordinates of the handle that points the specified text index in a 412da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi * text view. 413da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi */ 414da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi private static final class HandleCoordinates implements CoordinatesProvider { 415c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi // Must be larger than Editor#LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS. 416c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi private final static float LINE_SLOP_MULTIPLIER = 0.6f; 417da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi private final TextView mTextView; 418da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi private final Handle mHandleType; 419da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi private final int mIndex; 420f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private final boolean mPrimary; 421da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi private final String mActionDescription; 422da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi 4237557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader public HandleCoordinates(TextView textView, Handle handleType, int index, boolean primary) { 424da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi mTextView = textView; 425da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi mHandleType = handleType; 426da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi mIndex = index; 427f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi mPrimary = primary; 428da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi mActionDescription = "Could not locate " + handleType.toString() 429f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi + " handle that points text index: " + index 430f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi + " (" + (primary ? "primary" : "secondary" ) + ")"; 431da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } 432da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi 433da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi @Override 434da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi public float[] calculateCoordinates(View view) { 435da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi try { 436da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi return locateHandlePointsTextIndex(view); 437da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } catch (StringIndexOutOfBoundsException e) { 438da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi throw new PerformException.Builder() 439da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi .withActionDescription(mActionDescription) 440da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi .withViewDescription(HumanReadables.describe(view)) 441da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi .withCause(e) 442da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi .build(); 443da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } 444da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } 445da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi 446da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi private float[] locateHandlePointsTextIndex(View view) { 447f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi if (!(view instanceof HandleView)) { 448f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi throw new PerformException.Builder() 449f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withActionDescription(mActionDescription + " The view is not a HandleView") 450f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .withViewDescription(HumanReadables.describe(view)) 451f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi .build(); 452f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 453f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final HandleView handleView = (HandleView) view; 454da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi final int currentOffset = mHandleType == Handle.SELECTION_START ? 455da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi mTextView.getSelectionStart() : mTextView.getSelectionEnd(); 456c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi 457c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi final Layout layout = mTextView.getLayout(); 458f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 459c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi final int currentLine = layout.getLineForOffset(currentOffset); 460c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi final int targetLine = layout.getLineForOffset(mIndex); 461f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final float currentX = handleView.getHorizontal(layout, currentOffset); 462f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi final float currentY = layout.getLineTop(currentLine); 463da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi final float[] currentCoordinates = 4647557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader TextCoordinates.convertToScreenCoordinates(mTextView, currentX, currentY); 465da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi final float[] targetCoordinates = 4667557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader (new TextCoordinates(mIndex, mPrimary)).calculateCoordinates(mTextView); 467da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi final Rect bounds = new Rect(); 468da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi view.getBoundsOnScreen(bounds); 4695f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi final Rect visibleDisplayBounds = new Rect(); 4705f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi mTextView.getWindowVisibleDisplayFrame(visibleDisplayBounds); 4715f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi visibleDisplayBounds.right -= 1; 4725f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi visibleDisplayBounds.bottom -= 1; 4735f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi if (!visibleDisplayBounds.intersect(bounds)) { 4745f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi throw new PerformException.Builder() 4755f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi .withActionDescription(mActionDescription 4765f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi + " The handle is entirely out of the visible display frame of" 4775f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi + "the TextView's window.") 4785f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi .withViewDescription(HumanReadables.describe(view)) 4795f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi .build(); 4805f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi } 4815f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi final float dragPointX = Math.max(Math.min(bounds.centerX(), 4825f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi visibleDisplayBounds.right), visibleDisplayBounds.left); 4835f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi final float diffX = dragPointX - currentCoordinates[0]; 484c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi final float verticalOffset = bounds.height() * 0.7f; 4855f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi final float dragPointY = Math.max(Math.min(bounds.top + verticalOffset, 4865f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi visibleDisplayBounds.bottom), visibleDisplayBounds.top); 4875f71b5afe83ea6a183a9a010c05ce4e1453e264bKeisuke Kuroyanagi float diffY = dragPointY - currentCoordinates[1]; 488c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi if (currentLine > targetLine) { 489c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi diffY -= mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER; 490c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi } else if (currentLine < targetLine) { 491c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi diffY += mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER; 492c24c2bbb69634435d1d9fc97671229df245bd8d0Keisuke Kuroyanagi } 493da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi return new float[] {targetCoordinates[0] + diffX, targetCoordinates[1] + diffY}; 494da79ee683d74f2ad7d29147a30f3da17dd0ea6e2Keisuke Kuroyanagi } 49588cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi } 49688cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi 49788cabede9bc74ade7d0124cde3d40fadb6c97a85Keisuke Kuroyanagi /** 49890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * A provider of the x, y coordinates of the text at the specified index in a text view. 49990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki */ 50090cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki private static final class TextCoordinates implements CoordinatesProvider { 50190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 50290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki private final int mIndex; 503f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private final boolean mPrimary; 50490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki private final String mActionDescription; 50590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 50690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki public TextCoordinates(int index) { 5077557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader this(index, true); 508f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 509f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 5107557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader public TextCoordinates(int index, boolean primary) { 51190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki mIndex = index; 512f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi mPrimary = primary; 513f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi mActionDescription = "Could not locate text at index: " + mIndex 5147557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader + " (" + (primary ? "primary" : "secondary" ) + ")"; 51590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } 51690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 51790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki @Override 51890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki public float[] calculateCoordinates(View view) { 51990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki try { 5207557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader return locateTextAtIndex((TextView) view, mIndex, mPrimary); 52190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } catch (ClassCastException e) { 52290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki throw new PerformException.Builder() 52390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .withActionDescription(mActionDescription) 52490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .withViewDescription(HumanReadables.describe(view)) 52590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .withCause(e) 52690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .build(); 52790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } catch (StringIndexOutOfBoundsException e) { 52890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki throw new PerformException.Builder() 52990cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .withActionDescription(mActionDescription) 53090cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .withViewDescription(HumanReadables.describe(view)) 53190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .withCause(e) 53290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki .build(); 53390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } 53490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } 53590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki 53690cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki /** 53790cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki * @throws StringIndexOutOfBoundsException 53890cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki */ 5397557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader private float[] locateTextAtIndex(TextView textView, int index, boolean primary) { 54090cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki if (index < 0 || index > textView.getText().length()) { 54190cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki throw new StringIndexOutOfBoundsException(index); 54290cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } 54390cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki final Layout layout = textView.getLayout(); 5447557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader final int line = layout.getLineForOffset(index); 545f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return convertToScreenCoordinates(textView, 5467557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader (primary ? layout.getPrimaryHorizontal(index) 5477557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader : layout.getSecondaryHorizontal(index)), 548f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi layout.getLineTop(line)); 549f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 550f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 5517557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader /** 5527557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader * Convert TextView's local coordinates to on screen coordinates. 5537557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader * @param textView the TextView 5547557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader * @param x local horizontal coordinate 5557557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader * @param y local vertical coordinate 5567557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader * @return 5577557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader */ 5587557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader public static float[] convertToScreenCoordinates(TextView textView, float x, float y) { 5597557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader final int[] xy = new int[2]; 5607557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader textView.getLocationOnScreen(xy); 5617557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader return new float[]{ x + textView.getTotalPaddingLeft() - textView.getScrollX() + xy[0], 5627557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader y + textView.getTotalPaddingTop() - textView.getScrollY() + xy[1] }; 5637557a5a090204dccfd32096af4a6ded1f9bb5b17Roozbeh Pournader } 56490cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki } 56590cdfe0514154bbc008d39f1c99f7a1d2684446cAbodunrinwa Toki} 566