• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.text.method;
18 
19 import android.content.Intent;
20 import android.net.Uri;
21 import android.view.KeyEvent;
22 import android.view.MotionEvent;
23 import android.text.*;
24 import android.text.style.*;
25 import android.view.View;
26 import android.widget.TextView;
27 
28 public class
29 LinkMovementMethod
30 extends ScrollingMovementMethod
31 {
32     private static final int CLICK = 1;
33     private static final int UP = 2;
34     private static final int DOWN = 3;
35 
36     @Override
onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event)37     public boolean onKeyDown(TextView widget, Spannable buffer,
38                              int keyCode, KeyEvent event) {
39         switch (keyCode) {
40         case KeyEvent.KEYCODE_DPAD_CENTER:
41         case KeyEvent.KEYCODE_ENTER:
42             if (event.getRepeatCount() == 0) {
43                 if (action(CLICK, widget, buffer)) {
44                     return true;
45                 }
46             }
47         }
48 
49         return super.onKeyDown(widget, buffer, keyCode, event);
50     }
51 
52     @Override
up(TextView widget, Spannable buffer)53     protected boolean up(TextView widget, Spannable buffer) {
54         if (action(UP, widget, buffer)) {
55             return true;
56         }
57 
58         return super.up(widget, buffer);
59     }
60 
61     @Override
down(TextView widget, Spannable buffer)62     protected boolean down(TextView widget, Spannable buffer) {
63         if (action(DOWN, widget, buffer)) {
64             return true;
65         }
66 
67         return super.down(widget, buffer);
68     }
69 
70     @Override
left(TextView widget, Spannable buffer)71     protected boolean left(TextView widget, Spannable buffer) {
72         if (action(UP, widget, buffer)) {
73             return true;
74         }
75 
76         return super.left(widget, buffer);
77     }
78 
79     @Override
right(TextView widget, Spannable buffer)80     protected boolean right(TextView widget, Spannable buffer) {
81         if (action(DOWN, widget, buffer)) {
82             return true;
83         }
84 
85         return super.right(widget, buffer);
86     }
87 
action(int what, TextView widget, Spannable buffer)88     private boolean action(int what, TextView widget, Spannable buffer) {
89         boolean handled = false;
90 
91         Layout layout = widget.getLayout();
92 
93         int padding = widget.getTotalPaddingTop() +
94                       widget.getTotalPaddingBottom();
95         int areatop = widget.getScrollY();
96         int areabot = areatop + widget.getHeight() - padding;
97 
98         int linetop = layout.getLineForVertical(areatop);
99         int linebot = layout.getLineForVertical(areabot);
100 
101         int first = layout.getLineStart(linetop);
102         int last = layout.getLineEnd(linebot);
103 
104         ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);
105 
106         int a = Selection.getSelectionStart(buffer);
107         int b = Selection.getSelectionEnd(buffer);
108 
109         int selStart = Math.min(a, b);
110         int selEnd = Math.max(a, b);
111 
112         if (selStart < 0) {
113             if (buffer.getSpanStart(FROM_BELOW) >= 0) {
114                 selStart = selEnd = buffer.length();
115             }
116         }
117 
118         if (selStart > last)
119             selStart = selEnd = Integer.MAX_VALUE;
120         if (selEnd < first)
121             selStart = selEnd = -1;
122 
123         switch (what) {
124         case CLICK:
125             if (selStart == selEnd) {
126                 return false;
127             }
128 
129             ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
130 
131             if (link.length != 1)
132                 return false;
133 
134             link[0].onClick(widget);
135             break;
136 
137         case UP:
138             int beststart, bestend;
139 
140             beststart = -1;
141             bestend = -1;
142 
143             for (int i = 0; i < candidates.length; i++) {
144                 int end = buffer.getSpanEnd(candidates[i]);
145 
146                 if (end < selEnd || selStart == selEnd) {
147                     if (end > bestend) {
148                         beststart = buffer.getSpanStart(candidates[i]);
149                         bestend = end;
150                     }
151                 }
152             }
153 
154             if (beststart >= 0) {
155                 Selection.setSelection(buffer, bestend, beststart);
156                 return true;
157             }
158 
159             break;
160 
161         case DOWN:
162             beststart = Integer.MAX_VALUE;
163             bestend = Integer.MAX_VALUE;
164 
165             for (int i = 0; i < candidates.length; i++) {
166                 int start = buffer.getSpanStart(candidates[i]);
167 
168                 if (start > selStart || selStart == selEnd) {
169                     if (start < beststart) {
170                         beststart = start;
171                         bestend = buffer.getSpanEnd(candidates[i]);
172                     }
173                 }
174             }
175 
176             if (bestend < Integer.MAX_VALUE) {
177                 Selection.setSelection(buffer, beststart, bestend);
178                 return true;
179             }
180 
181             break;
182         }
183 
184         return false;
185     }
186 
onKeyUp(TextView widget, Spannable buffer, int keyCode, KeyEvent event)187     public boolean onKeyUp(TextView widget, Spannable buffer,
188                            int keyCode, KeyEvent event) {
189         return false;
190     }
191 
192     @Override
onTouchEvent(TextView widget, Spannable buffer, MotionEvent event)193     public boolean onTouchEvent(TextView widget, Spannable buffer,
194                                 MotionEvent event) {
195         int action = event.getAction();
196 
197         if (action == MotionEvent.ACTION_UP ||
198             action == MotionEvent.ACTION_DOWN) {
199             int x = (int) event.getX();
200             int y = (int) event.getY();
201 
202             x -= widget.getTotalPaddingLeft();
203             y -= widget.getTotalPaddingTop();
204 
205             x += widget.getScrollX();
206             y += widget.getScrollY();
207 
208             Layout layout = widget.getLayout();
209             int line = layout.getLineForVertical(y);
210             int off = layout.getOffsetForHorizontal(line, x);
211 
212             ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
213 
214             if (link.length != 0) {
215                 if (action == MotionEvent.ACTION_UP) {
216                     link[0].onClick(widget);
217                 } else if (action == MotionEvent.ACTION_DOWN) {
218                     Selection.setSelection(buffer,
219                                            buffer.getSpanStart(link[0]),
220                                            buffer.getSpanEnd(link[0]));
221                 }
222 
223                 return true;
224             } else {
225                 Selection.removeSelection(buffer);
226             }
227         }
228 
229         return super.onTouchEvent(widget, buffer, event);
230     }
231 
initialize(TextView widget, Spannable text)232     public void initialize(TextView widget, Spannable text) {
233         Selection.removeSelection(text);
234         text.removeSpan(FROM_BELOW);
235     }
236 
onTakeFocus(TextView view, Spannable text, int dir)237     public void onTakeFocus(TextView view, Spannable text, int dir) {
238         Selection.removeSelection(text);
239 
240         if ((dir & View.FOCUS_BACKWARD) != 0) {
241             text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT);
242         } else {
243             text.removeSpan(FROM_BELOW);
244         }
245     }
246 
getInstance()247     public static MovementMethod getInstance() {
248         if (sInstance == null)
249             sInstance = new LinkMovementMethod();
250 
251         return sInstance;
252     }
253 
254     private static LinkMovementMethod sInstance;
255     private static Object FROM_BELOW = new NoCopySpan.Concrete();
256 }
257