• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.text.Layout;
20 import android.text.Layout.Alignment;
21 import android.text.NoCopySpan;
22 import android.text.Spannable;
23 import android.view.KeyEvent;
24 import android.view.MotionEvent;
25 import android.view.ViewConfiguration;
26 import android.widget.TextView;
27 
28 @android.ravenwood.annotation.RavenwoodKeepWholeClass
29 public class Touch {
Touch()30     private Touch() { }
31 
32     /**
33      * Scrolls the specified widget to the specified coordinates, except
34      * constrains the X scrolling position to the horizontal regions of
35      * the text that will be visible after scrolling to the specified
36      * Y position.
37      */
scrollTo(TextView widget, Layout layout, int x, int y)38     public static void scrollTo(TextView widget, Layout layout, int x, int y) {
39         final int horizontalPadding = widget.getTotalPaddingLeft() + widget.getTotalPaddingRight();
40         final int availableWidth = widget.getWidth() - horizontalPadding;
41 
42         final int top = layout.getLineForVertical(y);
43         Alignment a = layout.getParagraphAlignment(top);
44         boolean ltr = layout.getParagraphDirection(top) > 0;
45 
46         int left, right;
47         if (widget.getHorizontallyScrolling()) {
48             final int verticalPadding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
49             final int bottom = layout.getLineForVertical(y + widget.getHeight() - verticalPadding);
50 
51             left = Integer.MAX_VALUE;
52             right = 0;
53 
54             for (int i = top; i <= bottom; i++) {
55                 left = (int) Math.min(left, layout.getLineLeft(i));
56                 right = (int) Math.max(right, layout.getLineRight(i));
57             }
58         } else {
59             left = 0;
60             right = availableWidth;
61         }
62 
63         final int actualWidth = right - left;
64 
65         if (actualWidth < availableWidth) {
66             if (a == Alignment.ALIGN_CENTER) {
67                 x = left - ((availableWidth - actualWidth) / 2);
68             } else if ((ltr && (a == Alignment.ALIGN_OPPOSITE)) ||
69                        (!ltr && (a == Alignment.ALIGN_NORMAL)) ||
70                        (a == Alignment.ALIGN_RIGHT)) {
71                 // align_opposite does NOT mean align_right, we need the paragraph
72                 // direction to resolve it to left or right
73                 x = left - (availableWidth - actualWidth);
74             } else {
75                 x = left;
76             }
77         } else {
78             x = Math.min(x, right - availableWidth);
79             x = Math.max(x, left);
80         }
81 
82         widget.scrollTo(x, y);
83     }
84 
85     /**
86      * Handles touch events for dragging.  You may want to do other actions
87      * like moving the cursor on touch as well.
88      */
onTouchEvent(TextView widget, Spannable buffer, MotionEvent event)89     public static boolean onTouchEvent(TextView widget, Spannable buffer,
90                                        MotionEvent event) {
91         DragState[] ds;
92 
93         switch (event.getActionMasked()) {
94         case MotionEvent.ACTION_DOWN:
95             ds = buffer.getSpans(0, buffer.length(), DragState.class);
96 
97             for (int i = 0; i < ds.length; i++) {
98                 buffer.removeSpan(ds[i]);
99             }
100 
101             buffer.setSpan(new DragState(event.getX(), event.getY(),
102                             widget.getScrollX(), widget.getScrollY()),
103                     0, 0, Spannable.SPAN_MARK_MARK);
104             return true;
105 
106         case MotionEvent.ACTION_UP:
107             ds = buffer.getSpans(0, buffer.length(), DragState.class);
108 
109             for (int i = 0; i < ds.length; i++) {
110                 buffer.removeSpan(ds[i]);
111             }
112 
113             if (ds.length > 0 && ds[0].mUsed) {
114                 return true;
115             } else {
116                 return false;
117             }
118 
119         case MotionEvent.ACTION_MOVE:
120             ds = buffer.getSpans(0, buffer.length(), DragState.class);
121 
122             if (ds.length > 0) {
123                 if (ds[0].mFarEnough == false) {
124                     int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
125 
126                     if (Math.abs(event.getX() - ds[0].mX) >= slop ||
127                         Math.abs(event.getY() - ds[0].mY) >= slop) {
128                         ds[0].mFarEnough = true;
129                     }
130                 }
131 
132                 if (ds[0].mFarEnough) {
133                     ds[0].mUsed = true;
134                     boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0
135                             || MetaKeyKeyListener.getMetaState(buffer,
136                                     MetaKeyKeyListener.META_SHIFT_ON) == 1
137                             || MetaKeyKeyListener.getMetaState(buffer,
138                                     MetaKeyKeyListener.META_SELECTING) != 0;
139 
140                     float dx;
141                     float dy;
142                     if (cap) {
143                         // if we're selecting, we want the scroll to go in
144                         // the direction of the drag
145                         dx = event.getX() - ds[0].mX;
146                         dy = event.getY() - ds[0].mY;
147                     } else {
148                         dx = ds[0].mX - event.getX();
149                         dy = ds[0].mY - event.getY();
150                     }
151                     ds[0].mX = event.getX();
152                     ds[0].mY = event.getY();
153 
154                     int nx = widget.getScrollX() + (int) dx;
155                     int ny = widget.getScrollY() + (int) dy;
156 
157                     int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
158                     Layout layout = widget.getLayout();
159 
160                     ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - padding));
161                     ny = Math.max(ny, 0);
162 
163                     int oldX = widget.getScrollX();
164                     int oldY = widget.getScrollY();
165 
166                     scrollTo(widget, layout, nx, ny);
167 
168                     // If we actually scrolled, then cancel the up action.
169                     if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
170                         widget.cancelLongPress();
171                     }
172 
173                     return true;
174                 }
175             }
176         }
177 
178         return false;
179     }
180 
181     /**
182      * @param widget The text view.
183      * @param buffer The text buffer.
184      */
getInitialScrollX(TextView widget, Spannable buffer)185     public static int getInitialScrollX(TextView widget, Spannable buffer) {
186         DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class);
187         return ds.length > 0 ? ds[0].mScrollX : -1;
188     }
189 
190     /**
191      * @param widget The text view.
192      * @param buffer The text buffer.
193      */
getInitialScrollY(TextView widget, Spannable buffer)194     public static int getInitialScrollY(TextView widget, Spannable buffer) {
195         DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class);
196         return ds.length > 0 ? ds[0].mScrollY : -1;
197     }
198 
199     private static class DragState implements NoCopySpan {
200         public float mX;
201         public float mY;
202         public int mScrollX;
203         public int mScrollY;
204         public boolean mFarEnough;
205         public boolean mUsed;
206 
DragState(float x, float y, int scrollX, int scrollY)207         public DragState(float x, float y, int scrollX, int scrollY) {
208             mX = x;
209             mY = y;
210             mScrollX = scrollX;
211             mScrollY = scrollY;
212         }
213     }
214 }
215