ArrowKeyMovementMethod.java revision a6e50454890629fe369538d8e473945bc9d68136
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text.method; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent; 211c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackbornimport android.graphics.Rect; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.*; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.TextView; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View; 25a6e50454890629fe369538d8e473945bc9d68136Eric Fischerimport android.view.ViewConfiguration; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// XXX this doesn't extend MetaKeyKeyListener because the signatures 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// don't match. Need to figure that out. Meanwhile the meta keys 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// won't work in fields that don't take input. 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectArrowKeyMovementMethod 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimplements MovementMethod 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{ 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean up(TextView widget, Spannable buffer) { 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_SHIFT_ON) == 1) || 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (MetaKeyKeyListener.getMetaState(buffer, 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.META_SELECTING) != 0); 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alt = MetaKeyKeyListener.getMetaState(buffer, 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_ALT_ON) == 1; 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.extendSelection(buffer, 0); 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendUp(buffer, layout); 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(buffer, 0); 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveUp(buffer, layout); 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean down(TextView widget, Spannable buffer) { 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_SHIFT_ON) == 1) || 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (MetaKeyKeyListener.getMetaState(buffer, 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.META_SELECTING) != 0); 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alt = MetaKeyKeyListener.getMetaState(buffer, 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_ALT_ON) == 1; 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.extendSelection(buffer, buffer.length()); 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendDown(buffer, layout); 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(buffer, buffer.length()); 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveDown(buffer, layout); 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean left(TextView widget, Spannable buffer) { 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_SHIFT_ON) == 1) || 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (MetaKeyKeyListener.getMetaState(buffer, 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.META_SELECTING) != 0); 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alt = MetaKeyKeyListener.getMetaState(buffer, 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_ALT_ON) == 1; 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendToLeftEdge(buffer, layout); 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendLeft(buffer, layout); 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveToLeftEdge(buffer, layout); 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveLeft(buffer, layout); 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean right(TextView widget, Spannable buffer) { 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_SHIFT_ON) == 1) || 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (MetaKeyKeyListener.getMetaState(buffer, 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.META_SELECTING) != 0); 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alt = MetaKeyKeyListener.getMetaState(buffer, 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_ALT_ON) == 1; 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendToRightEdge(buffer, layout); 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendRight(buffer, layout); 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveToRightEdge(buffer, layout); 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveRight(buffer, layout); 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (executeDown(widget, buffer, keyCode)) { 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.resetLockedMeta(buffer); 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = false; 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (keyCode) { 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_UP: 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= up(widget, buffer); 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_DOWN: 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= down(widget, buffer); 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_LEFT: 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= left(widget, buffer); 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_RIGHT: 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= right(widget, buffer); 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_CENTER: 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (widget.showContextMenu()) { 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled = true; 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (handled) { 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.resetLockedMeta(buffer); 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyUp(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int code = event.getKeyCode(); 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (code != KeyEvent.KEYCODE_UNKNOWN 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project && event.getAction() == KeyEvent.ACTION_MULTIPLE) { 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int repeat = event.getRepeatCount(); 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = false; 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while ((--repeat) > 0) { 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= executeDown(view, text, code); 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onTrackballEvent(TextView widget, Spannable text, 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MotionEvent event) { 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onTouchEvent(TextView widget, Spannable buffer, 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MotionEvent event) { 2071c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn int initialScrollX = -1, initialScrollY = -1; 2081c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn if (event.getAction() == MotionEvent.ACTION_UP) { 2091c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn initialScrollX = Touch.getInitialScrollX(widget, buffer); 2101c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn initialScrollY = Touch.getInitialScrollY(widget, buffer); 2111c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } 2121c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = Touch.onTouchEvent(widget, buffer, event); 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 215b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project if (widget.isFocused() && !widget.didTouchFocusSelect()) { 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (event.getAction() == MotionEvent.ACTION_UP) { 2171c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // If we have scrolled, then the up shouldn't move the cursor, 2181c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // but we do need to make sure the cursor is still visible at 2191c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // the current scroll offset to avoid the scroll jumping later 2201c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // to show it. 2211c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) || 2221c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) { 2231c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn widget.moveCursorToVisibleOffset(); 2241c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn return true; 2251c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } 2261c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int x = (int) event.getX(); 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int y = (int) event.getY(); 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x -= widget.getTotalPaddingLeft(); 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project y -= widget.getTotalPaddingTop(); 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2331c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // Clamp the position to inside of the view. 2341c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn if (x < 0) { 2351c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn x = 0; 2361c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } else if (x >= (widget.getWidth()-widget.getTotalPaddingRight())) { 2371c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn x = widget.getWidth()-widget.getTotalPaddingRight() - 1; 2381c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } 2391c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn if (y < 0) { 2401c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn y = 0; 2411c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } else if (y >= (widget.getHeight()-widget.getTotalPaddingBottom())) { 2421c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn y = widget.getHeight()-widget.getTotalPaddingBottom() - 1; 2431c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } 2441c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x += widget.getScrollX(); 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project y += widget.getScrollY(); 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = layout.getLineForVertical(y); 2501c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int off = layout.getOffsetForHorizontal(line, x); 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2531c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // XXX should do the same adjust for x as we do for the line. 2541c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project KeyEvent.META_SHIFT_ON) == 1) || 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (MetaKeyKeyListener.getMetaState(buffer, 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.META_SELECTING) != 0); 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 260a6e50454890629fe369538d8e473945bc9d68136Eric Fischer DoubleTapState[] tap = buffer.getSpans(0, buffer.length(), 261a6e50454890629fe369538d8e473945bc9d68136Eric Fischer DoubleTapState.class); 262a6e50454890629fe369538d8e473945bc9d68136Eric Fischer boolean doubletap = false; 263a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 264a6e50454890629fe369538d8e473945bc9d68136Eric Fischer if (tap.length > 0) { 265a6e50454890629fe369538d8e473945bc9d68136Eric Fischer if (event.getEventTime() - tap[0].mWhen <= 266a6e50454890629fe369538d8e473945bc9d68136Eric Fischer ViewConfiguration.getDoubleTapTimeout()) { 267a6e50454890629fe369538d8e473945bc9d68136Eric Fischer if (sameWord(buffer, off, Selection.getSelectionEnd(buffer))) { 268a6e50454890629fe369538d8e473945bc9d68136Eric Fischer doubletap = true; 269a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 270a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 271a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 272a6e50454890629fe369538d8e473945bc9d68136Eric Fischer tap[0].mWhen = event.getEventTime(); 273a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } else { 274a6e50454890629fe369538d8e473945bc9d68136Eric Fischer DoubleTapState newtap = new DoubleTapState(); 275a6e50454890629fe369538d8e473945bc9d68136Eric Fischer newtap.mWhen = event.getEventTime(); 276a6e50454890629fe369538d8e473945bc9d68136Eric Fischer buffer.setSpan(newtap, 0, buffer.length(), 277a6e50454890629fe369538d8e473945bc9d68136Eric Fischer Spannable.SPAN_INCLUSIVE_INCLUSIVE); 278a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 279a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.extendSelection(buffer, off); 282a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } else if (doubletap) { 283a6e50454890629fe369538d8e473945bc9d68136Eric Fischer Selection.setSelection(buffer, 284a6e50454890629fe369538d8e473945bc9d68136Eric Fischer findWordStart(buffer, off), 285a6e50454890629fe369538d8e473945bc9d68136Eric Fischer findWordEnd(buffer, off)); 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(buffer, off); 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.resetLockedMeta(buffer); 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 300a6e50454890629fe369538d8e473945bc9d68136Eric Fischer private static class DoubleTapState implements NoCopySpan { 301a6e50454890629fe369538d8e473945bc9d68136Eric Fischer long mWhen; 302a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 303a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 304a6e50454890629fe369538d8e473945bc9d68136Eric Fischer private static boolean sameWord(CharSequence text, int one, int two) { 305a6e50454890629fe369538d8e473945bc9d68136Eric Fischer int start = findWordStart(text, one); 306a6e50454890629fe369538d8e473945bc9d68136Eric Fischer int end = findWordEnd(text, one); 307a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 308a6e50454890629fe369538d8e473945bc9d68136Eric Fischer if (end == start) { 309a6e50454890629fe369538d8e473945bc9d68136Eric Fischer return false; 310a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 311a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 312a6e50454890629fe369538d8e473945bc9d68136Eric Fischer return start == findWordStart(text, two) && 313a6e50454890629fe369538d8e473945bc9d68136Eric Fischer end == findWordEnd(text, two); 314a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 315a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 316a6e50454890629fe369538d8e473945bc9d68136Eric Fischer // TODO: Unify with TextView.getWordForDictionary() 317a6e50454890629fe369538d8e473945bc9d68136Eric Fischer private static int findWordStart(CharSequence text, int start) { 318a6e50454890629fe369538d8e473945bc9d68136Eric Fischer for (; start > 0; start--) { 319a6e50454890629fe369538d8e473945bc9d68136Eric Fischer char c = text.charAt(start - 1); 320a6e50454890629fe369538d8e473945bc9d68136Eric Fischer int type = Character.getType(c); 321a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 322a6e50454890629fe369538d8e473945bc9d68136Eric Fischer if (c != '\'' && 323a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.UPPERCASE_LETTER && 324a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.LOWERCASE_LETTER && 325a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.TITLECASE_LETTER && 326a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.MODIFIER_LETTER && 327a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.DECIMAL_DIGIT_NUMBER) { 328a6e50454890629fe369538d8e473945bc9d68136Eric Fischer break; 329a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 330a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 331a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 332a6e50454890629fe369538d8e473945bc9d68136Eric Fischer return start; 333a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 334a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 335a6e50454890629fe369538d8e473945bc9d68136Eric Fischer // TODO: Unify with TextView.getWordForDictionary() 336a6e50454890629fe369538d8e473945bc9d68136Eric Fischer private static int findWordEnd(CharSequence text, int end) { 337a6e50454890629fe369538d8e473945bc9d68136Eric Fischer int len = text.length(); 338a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 339a6e50454890629fe369538d8e473945bc9d68136Eric Fischer for (; end < len; end++) { 340a6e50454890629fe369538d8e473945bc9d68136Eric Fischer char c = text.charAt(end); 341a6e50454890629fe369538d8e473945bc9d68136Eric Fischer int type = Character.getType(c); 342a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 343a6e50454890629fe369538d8e473945bc9d68136Eric Fischer if (c != '\'' && 344a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.UPPERCASE_LETTER && 345a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.LOWERCASE_LETTER && 346a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.TITLECASE_LETTER && 347a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.MODIFIER_LETTER && 348a6e50454890629fe369538d8e473945bc9d68136Eric Fischer type != Character.DECIMAL_DIGIT_NUMBER) { 349a6e50454890629fe369538d8e473945bc9d68136Eric Fischer break; 350a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 351a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 352a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 353a6e50454890629fe369538d8e473945bc9d68136Eric Fischer return end; 354a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 355a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean canSelectArbitrarily() { 3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void initialize(TextView widget, Spannable text) { 3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, 0); 3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onTakeFocus(TextView view, Spannable text, int dir) { 3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) { 3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = view.getLayout(); 3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (layout == null) { 3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* 3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This shouldn't be null, but do something sensible if it is. 3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, text.length()); 3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* 3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Put the cursor at the end of the first line, which is 3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * either the last offset if there is only one line, or the 3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * offset before the first character of the second line 3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * if there is more than one line. 3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (layout.getLineCount() == 1) { 3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, text.length()); 3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, layout.getLineStart(1) - 1); 3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, text.length()); 3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static MovementMethod getInstance() { 3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sInstance == null) 3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sInstance = new ArrowKeyMovementMethod(); 3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return sInstance; 3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static ArrowKeyMovementMethod sInstance; 3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 400