ArrowKeyMovementMethod.java revision 2703a42d16af0e62da1bba02b6c935d98debf936
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 19b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunneimport android.text.Layout; 20b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunneimport android.text.Selection; 21b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunneimport android.text.Spannable; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent; 24b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunneimport android.view.View; 25b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunneimport android.widget.TextView; 26b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunneimport android.widget.TextView.CursorController; 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 32b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunnepublic class ArrowKeyMovementMethod implements MovementMethod { 33b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne /** 34b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne * An optional controller for the cursor. 35b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne * Use {@link #setCursorController(CursorController)} to set this field. 36b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne */ 37b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne protected CursorController mCursorController; 38b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne 39b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne private boolean isCap(Spannable buffer) { 40b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) || 41b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0)); 42b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } 43b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne 44b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne private boolean isAlt(Spannable buffer) { 45b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_ALT_ON) == 1; 46b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } 47b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean up(TextView widget, Spannable buffer) { 49b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean cap = isCap(buffer); 50b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean alt = isAlt(buffer); 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.extendSelection(buffer, 0); 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendUp(buffer, layout); 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(buffer, 0); 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 65ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett return Selection.moveUp(buffer, layout); 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean down(TextView widget, Spannable buffer) { 71b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean cap = isCap(buffer); 72b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean alt = isAlt(buffer); 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.extendSelection(buffer, buffer.length()); 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendDown(buffer, layout); 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(buffer, buffer.length()); 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 87ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett return Selection.moveDown(buffer, layout); 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean left(TextView widget, Spannable buffer) { 93b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean cap = isCap(buffer); 94b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean alt = isAlt(buffer); 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) { 113b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean cap = isCap(buffer); 114b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean alt = isAlt(buffer); 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Layout layout = widget.getLayout(); 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cap) { 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendToRightEdge(buffer, layout); 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.extendRight(buffer, layout); 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alt) { 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveToRightEdge(buffer, layout); 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return Selection.moveRight(buffer, layout); 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (executeDown(widget, buffer, keyCode)) { 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.resetLockedMeta(buffer); 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = false; 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (keyCode) { 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_UP: 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= up(widget, buffer); 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_DOWN: 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= down(widget, buffer); 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_LEFT: 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= left(widget, buffer); 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_RIGHT: 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= right(widget, buffer); 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case KeyEvent.KEYCODE_DPAD_CENTER: 163b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) && 164b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne (widget.showContextMenu())) { 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled = true; 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (handled) { 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.resetLockedMeta(buffer); 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyUp(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int code = event.getKeyCode(); 183b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if (code != KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) { 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int repeat = event.getRepeatCount(); 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = false; 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while ((--repeat) > 0) { 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled |= executeDown(view, text, code); 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 193ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett 194b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) { 195b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if (mCursorController != null) { 196b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne mCursorController.hide(); 197b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 200ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett 201b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { 202b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if (mCursorController != null) { 203b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return onTouchEventCursor(widget, buffer, event); 204b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } else { 205b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return onTouchEventStandard(widget, buffer, event); 206b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } 207b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } 208b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne 209b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) { 2101c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn int initialScrollX = -1, initialScrollY = -1; 2111c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn if (event.getAction() == MotionEvent.ACTION_UP) { 2121c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn initialScrollX = Touch.getInitialScrollX(widget, buffer); 2131c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn initialScrollY = Touch.getInitialScrollY(widget, buffer); 2141c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } 215ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = Touch.onTouchEvent(widget, buffer, event); 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 218b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project if (widget.isFocused() && !widget.didTouchFocusSelect()) { 219ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett if (event.getAction() == MotionEvent.ACTION_DOWN) { 220b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean cap = isCap(buffer); 221ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett if (cap) { 222b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne int offset = widget.getOffset((int) event.getX(), (int) event.getY()); 223b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne 224b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT); 225ab9289320f598509cf358523ba173d69178a55eaMaryam Garrett 226ab9289320f598509cf358523ba173d69178a55eaMaryam Garrett // Disallow intercepting of the touch events, so that 227ab9289320f598509cf358523ba173d69178a55eaMaryam Garrett // users can scroll and select at the same time. 228ab9289320f598509cf358523ba173d69178a55eaMaryam Garrett // without this, users would get booted out of select 229ab9289320f598509cf358523ba173d69178a55eaMaryam Garrett // mode once the view detected it needed to scroll. 230ab9289320f598509cf358523ba173d69178a55eaMaryam Garrett widget.getParent().requestDisallowInterceptTouchEvent(true); 231ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett } 2328cdb684163051c12f37e8a5f9031f17efd9d0fa4Kenny Root } else if (event.getAction() == MotionEvent.ACTION_MOVE) { 233b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne boolean cap = isCap(buffer); 23439f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett 2358cdb684163051c12f37e8a5f9031f17efd9d0fa4Kenny Root if (cap && handled) { 23639f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett // Before selecting, make sure we've moved out of the "slop". 23739f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett // handled will be true, if we're in select mode AND we're 23839f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett // OUT of the slop 23939f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett 24039f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett // Turn long press off while we're selecting. User needs to 241b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne // re-tap on the selection to enable long press 24239f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett widget.cancelLongPress(); 24339f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett 24439f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett // Update selection as we're moving the selection area. 24539f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett 24639f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett // Get the current touch position 247b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne int offset = widget.getOffset((int) event.getX(), (int) event.getY()); 248b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne 249b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne Selection.extendSelection(buffer, offset); 25039f0efba92a4420f77e3abc53c367ea3cacde3cfMaryam Garrett return true; 251ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett } 252ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett } else if (event.getAction() == MotionEvent.ACTION_UP) { 2531c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // If we have scrolled, then the up shouldn't move the cursor, 2541c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // but we do need to make sure the cursor is still visible at 2551c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // the current scroll offset to avoid the scroll jumping later 2561c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn // to show it. 2571c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) || 258b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) { 2591c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn widget.moveCursorToVisibleOffset(); 2601c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn return true; 2611c9aefd471cec85f905bea4099f4a641f347e0a0Dianne Hackborn } 262ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett 263b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne int offset = widget.getOffset((int) event.getX(), (int) event.getY()); 264b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if (isCap(buffer)) { 26562c4ad3b6ba162540c3fb57fcacb375ccfa53454Maryam Garrett buffer.removeSpan(LAST_TAP_DOWN); 266b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne Selection.extendSelection(buffer, offset); 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 268b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne Selection.setSelection(buffer, offset); 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MetaKeyKeyListener.resetLockedMeta(buffer); 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 281b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) { 282b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if (widget.isFocused() && !widget.didTouchFocusSelect()) { 283b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne switch (event.getActionMasked()) { 284b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne case MotionEvent.ACTION_MOVE: 285b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne widget.cancelLongPress(); 286a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 287b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne // Offset the current touch position (from controller to cursor) 288b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne final float x = event.getX() + mCursorController.getOffsetX(); 289b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne final float y = event.getY() + mCursorController.getOffsetY(); 2903e05a0beb2fad0b21558019d2adf6805da70e10eGilles Debunne mCursorController.updatePosition((int) x, (int) y); 291b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return true; 292a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 293b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne case MotionEvent.ACTION_UP: 294b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne case MotionEvent.ACTION_CANCEL: 295b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne mCursorController = null; 296b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return true; 297a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 298a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 299b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne return false; 300a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 301a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 302b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne /** 303b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne * Defines the cursor controller. 304b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne * 305b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne * When set, this object can be used to handle events, that can be translated in cursor updates. 306b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne * @param cursorController A cursor controller implementation 307b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne */ 308b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne public void setCursorController(CursorController cursorController) { 309b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne mCursorController = cursorController; 310a6e50454890629fe369538d8e473945bc9d68136Eric Fischer } 311a6e50454890629fe369538d8e473945bc9d68136Eric Fischer 3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean canSelectArbitrarily() { 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void initialize(TextView widget, Spannable text) { 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, 0); 3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onTakeFocus(TextView view, Spannable text, int dir) { 3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) { 3222703a42d16af0e62da1bba02b6c935d98debf936Gilles Debunne if (view.getLayout() == null) { 3232703a42d16af0e62da1bba02b6c935d98debf936Gilles Debunne // This shouldn't be null, but do something sensible if it is. 3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, text.length()); 3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Selection.setSelection(text, text.length()); 3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static MovementMethod getInstance() { 332b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne if (sInstance == null) { 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sInstance = new ArrowKeyMovementMethod(); 334b0d6ba1ec4f71b96cab7d1ff62b846d5cf162c4fGilles Debunne } 3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return sInstance; 3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 339ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett 340ce08379e22609415971ece6ba3417d6d3fd338d2Maryam Garrett private static final Object LAST_TAP_DOWN = new Object(); 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static ArrowKeyMovementMethod sInstance; 3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 343