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