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