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
1975b7a931bc96c1b46cbf3773cd75c30bf6d6f5f0Gilles Debunneimport android.text.Layout;
2075b7a931bc96c1b46cbf3773cd75c30bf6d6f5f0Gilles Debunneimport android.text.NoCopySpan;
2175b7a931bc96c1b46cbf3773cd75c30bf6d6f5f0Gilles Debunneimport android.text.Selection;
2275b7a931bc96c1b46cbf3773cd75c30bf6d6f5f0Gilles Debunneimport android.text.Spannable;
2375b7a931bc96c1b46cbf3773cd75c30bf6d6f5f0Gilles Debunneimport android.text.style.ClickableSpan;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.TextView;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown/**
3067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * A movement method that traverses links in the text buffer and scrolls if necessary.
3167b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown * Supports clicking on links with DPad Center or Enter.
3267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown */
3367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brownpublic class LinkMovementMethod extends ScrollingMovementMethod {
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CLICK = 1;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int UP = 2;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DOWN = 3;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
39cd0b01327bd80dc3306ffb28a4cefd5ca20c50b4Victoria Lease    public boolean canSelectArbitrarily() {
40cd0b01327bd80dc3306ffb28a4cefd5ca20c50b4Victoria Lease        return true;
41cd0b01327bd80dc3306ffb28a4cefd5ca20c50b4Victoria Lease    }
42cd0b01327bd80dc3306ffb28a4cefd5ca20c50b4Victoria Lease
43cd0b01327bd80dc3306ffb28a4cefd5ca20c50b4Victoria Lease    @Override
4467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown    protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode,
4567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown            int movementMetaState, KeyEvent event) {
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (keyCode) {
4767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown            case KeyEvent.KEYCODE_DPAD_CENTER:
4867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown            case KeyEvent.KEYCODE_ENTER:
4967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown                if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
50d9ed795ca51c57427ad27f246d5ec19f89ff508dGilles Debunne                    if (event.getAction() == KeyEvent.ACTION_DOWN &&
51d9ed795ca51c57427ad27f246d5ec19f89ff508dGilles Debunne                            event.getRepeatCount() == 0 && action(CLICK, widget, buffer)) {
52d9ed795ca51c57427ad27f246d5ec19f89ff508dGilles Debunne                        return true;
5367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown                    }
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5567b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown                break;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5767b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown        return super.handleMovementKey(widget, buffer, keyCode, movementMetaState, event);
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean up(TextView widget, Spannable buffer) {
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(UP, widget, buffer)) {
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.up(widget, buffer);
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean down(TextView widget, Spannable buffer) {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(DOWN, widget, buffer)) {
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.down(widget, buffer);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean left(TextView widget, Spannable buffer) {
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(UP, widget, buffer)) {
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.left(widget, buffer);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean right(TextView widget, Spannable buffer) {
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(DOWN, widget, buffer)) {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.right(widget, buffer);
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean action(int what, TextView widget, Spannable buffer) {
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Layout layout = widget.getLayout();
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int padding = widget.getTotalPaddingTop() +
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      widget.getTotalPaddingBottom();
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int areatop = widget.getScrollY();
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int areabot = areatop + widget.getHeight() - padding;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linetop = layout.getLineForVertical(areatop);
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linebot = layout.getLineForVertical(areabot);
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int first = layout.getLineStart(linetop);
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int last = layout.getLineEnd(linebot);
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int a = Selection.getSelectionStart(buffer);
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int b = Selection.getSelectionEnd(buffer);
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int selStart = Math.min(a, b);
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int selEnd = Math.max(a, b);
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (selStart < 0) {
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (buffer.getSpanStart(FROM_BELOW) >= 0) {
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selStart = selEnd = buffer.length();
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (selStart > last)
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selStart = selEnd = Integer.MAX_VALUE;
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (selEnd < first)
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selStart = selEnd = -1;
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (what) {
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case CLICK:
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart == selEnd) {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (link.length != 1)
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            link[0].onClick(widget);
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case UP:
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int beststart, bestend;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            beststart = -1;
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bestend = -1;
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < candidates.length; i++) {
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = buffer.getSpanEnd(candidates[i]);
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (end < selEnd || selStart == selEnd) {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (end > bestend) {
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        beststart = buffer.getSpanStart(candidates[i]);
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        bestend = end;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (beststart >= 0) {
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(buffer, bestend, beststart);
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case DOWN:
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            beststart = Integer.MAX_VALUE;
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bestend = Integer.MAX_VALUE;
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < candidates.length; i++) {
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = buffer.getSpanStart(candidates[i]);
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start > selStart || selStart == selEnd) {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (start < beststart) {
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        beststart = start;
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        bestend = buffer.getSpanEnd(candidates[i]);
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (bestend < Integer.MAX_VALUE) {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(buffer, beststart, bestend);
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onTouchEvent(TextView widget, Spannable buffer,
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                MotionEvent event) {
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int action = event.getAction();
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action == MotionEvent.ACTION_UP ||
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            action == MotionEvent.ACTION_DOWN) {
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int x = (int) event.getX();
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int y = (int) event.getY();
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x -= widget.getTotalPaddingLeft();
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            y -= widget.getTotalPaddingTop();
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x += widget.getScrollX();
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            y += widget.getScrollY();
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Layout layout = widget.getLayout();
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int line = layout.getLineForVertical(y);
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int off = layout.getOffsetForHorizontal(line, x);
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (link.length != 0) {
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (action == MotionEvent.ACTION_UP) {
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    link[0].onClick(widget);
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (action == MotionEvent.ACTION_DOWN) {
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Selection.setSelection(buffer,
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                           buffer.getSpanStart(link[0]),
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                           buffer.getSpanEnd(link[0]));
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.removeSelection(buffer);
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.onTouchEvent(widget, buffer, event);
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown    @Override
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void initialize(TextView widget, Spannable text) {
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Selection.removeSelection(text);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.removeSpan(FROM_BELOW);
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown    @Override
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onTakeFocus(TextView view, Spannable text, int dir) {
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Selection.removeSelection(text);
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((dir & View.FOCUS_BACKWARD) != 0) {
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT);
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.removeSpan(FROM_BELOW);
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static MovementMethod getInstance() {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sInstance == null)
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sInstance = new LinkMovementMethod();
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sInstance;
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static LinkMovementMethod sInstance;
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Object FROM_BELOW = new NoCopySpan.Concrete();
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
260