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