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