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
3967b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown    protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode,
4067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown            int movementMetaState, KeyEvent event) {
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (keyCode) {
4267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown            case KeyEvent.KEYCODE_DPAD_CENTER:
4367b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown            case KeyEvent.KEYCODE_ENTER:
4467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown                if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
45d9ed795ca51c57427ad27f246d5ec19f89ff508dGilles Debunne                    if (event.getAction() == KeyEvent.ACTION_DOWN &&
46d9ed795ca51c57427ad27f246d5ec19f89ff508dGilles Debunne                            event.getRepeatCount() == 0 && action(CLICK, widget, buffer)) {
47d9ed795ca51c57427ad27f246d5ec19f89ff508dGilles Debunne                        return true;
4867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown                    }
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5067b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown                break;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5267b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown        return super.handleMovementKey(widget, buffer, keyCode, movementMetaState, event);
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean up(TextView widget, Spannable buffer) {
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(UP, widget, buffer)) {
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.up(widget, buffer);
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean down(TextView widget, Spannable buffer) {
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(DOWN, widget, buffer)) {
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.down(widget, buffer);
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean left(TextView widget, Spannable buffer) {
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(UP, widget, buffer)) {
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.left(widget, buffer);
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean right(TextView widget, Spannable buffer) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action(DOWN, widget, buffer)) {
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.right(widget, buffer);
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean action(int what, TextView widget, Spannable buffer) {
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Layout layout = widget.getLayout();
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int padding = widget.getTotalPaddingTop() +
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      widget.getTotalPaddingBottom();
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int areatop = widget.getScrollY();
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int areabot = areatop + widget.getHeight() - padding;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linetop = layout.getLineForVertical(areatop);
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linebot = layout.getLineForVertical(areabot);
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int first = layout.getLineStart(linetop);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int last = layout.getLineEnd(linebot);
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int a = Selection.getSelectionStart(buffer);
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int b = Selection.getSelectionEnd(buffer);
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int selStart = Math.min(a, b);
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int selEnd = Math.max(a, b);
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (selStart < 0) {
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (buffer.getSpanStart(FROM_BELOW) >= 0) {
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selStart = selEnd = buffer.length();
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (selStart > last)
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selStart = selEnd = Integer.MAX_VALUE;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (selEnd < first)
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selStart = selEnd = -1;
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (what) {
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case CLICK:
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart == selEnd) {
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (link.length != 1)
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            link[0].onClick(widget);
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case UP:
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int beststart, bestend;
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            beststart = -1;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bestend = -1;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < candidates.length; i++) {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = buffer.getSpanEnd(candidates[i]);
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (end < selEnd || selStart == selEnd) {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (end > bestend) {
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        beststart = buffer.getSpanStart(candidates[i]);
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        bestend = end;
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (beststart >= 0) {
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(buffer, bestend, beststart);
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case DOWN:
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            beststart = Integer.MAX_VALUE;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bestend = Integer.MAX_VALUE;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < candidates.length; i++) {
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = buffer.getSpanStart(candidates[i]);
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start > selStart || selStart == selEnd) {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (start < beststart) {
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        beststart = start;
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        bestend = buffer.getSpanEnd(candidates[i]);
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (bestend < Integer.MAX_VALUE) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(buffer, beststart, bestend);
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onTouchEvent(TextView widget, Spannable buffer,
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                MotionEvent event) {
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int action = event.getAction();
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (action == MotionEvent.ACTION_UP ||
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            action == MotionEvent.ACTION_DOWN) {
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int x = (int) event.getX();
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int y = (int) event.getY();
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x -= widget.getTotalPaddingLeft();
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            y -= widget.getTotalPaddingTop();
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x += widget.getScrollX();
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            y += widget.getScrollY();
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Layout layout = widget.getLayout();
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int line = layout.getLineForVertical(y);
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int off = layout.getOffsetForHorizontal(line, x);
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (link.length != 0) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (action == MotionEvent.ACTION_UP) {
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    link[0].onClick(widget);
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (action == MotionEvent.ACTION_DOWN) {
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Selection.setSelection(buffer,
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                           buffer.getSpanStart(link[0]),
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                           buffer.getSpanEnd(link[0]));
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.removeSelection(buffer);
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.onTouchEvent(widget, buffer, event);
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22867b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown    @Override
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void initialize(TextView widget, Spannable text) {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Selection.removeSelection(text);
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.removeSpan(FROM_BELOW);
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23467b6ab72ae96a9f2be929de2c32c110df5390fddJeff Brown    @Override
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onTakeFocus(TextView view, Spannable text, int dir) {
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Selection.removeSelection(text);
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((dir & View.FOCUS_BACKWARD) != 0) {
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT);
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.removeSpan(FROM_BELOW);
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static MovementMethod getInstance() {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sInstance == null)
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sInstance = new LinkMovementMethod();
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sInstance;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static LinkMovementMethod sInstance;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Object FROM_BELOW = new NoCopySpan.Concrete();
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
255