Home | History | Annotate | Download | only in method
      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.text.Layout;
     20 import android.text.Selection;
     21 import android.text.Spannable;
     22 import android.view.KeyEvent;
     23 import android.view.MotionEvent;
     24 import android.view.View;
     25 import android.widget.TextView;
     26 
     27 // XXX this doesn't extend MetaKeyKeyListener because the signatures
     28 // don't match.  Need to figure that out.  Meanwhile the meta keys
     29 // won't work in fields that don't take input.
     30 
     31 public class ArrowKeyMovementMethod implements MovementMethod {
     32     private boolean isCap(Spannable buffer) {
     33         return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
     34                 (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
     35     }
     36 
     37     private boolean isAlt(Spannable buffer) {
     38         return MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_ALT_ON) == 1;
     39     }
     40 
     41     private boolean up(TextView widget, Spannable buffer) {
     42         boolean cap = isCap(buffer);
     43         boolean alt = isAlt(buffer);
     44         Layout layout = widget.getLayout();
     45 
     46         if (cap) {
     47             if (alt) {
     48                 Selection.extendSelection(buffer, 0);
     49                 return true;
     50             } else {
     51                 return Selection.extendUp(buffer, layout);
     52             }
     53         } else {
     54             if (alt) {
     55                 Selection.setSelection(buffer, 0);
     56                 return true;
     57             } else {
     58                 return Selection.moveUp(buffer, layout);
     59             }
     60         }
     61     }
     62 
     63     private boolean down(TextView widget, Spannable buffer) {
     64         boolean cap = isCap(buffer);
     65         boolean alt = isAlt(buffer);
     66         Layout layout = widget.getLayout();
     67 
     68         if (cap) {
     69             if (alt) {
     70                 Selection.extendSelection(buffer, buffer.length());
     71                 return true;
     72             } else {
     73                 return Selection.extendDown(buffer, layout);
     74             }
     75         } else {
     76             if (alt) {
     77                 Selection.setSelection(buffer, buffer.length());
     78                 return true;
     79             } else {
     80                 return Selection.moveDown(buffer, layout);
     81             }
     82         }
     83     }
     84 
     85     private boolean left(TextView widget, Spannable buffer) {
     86         boolean cap = isCap(buffer);
     87         boolean alt = isAlt(buffer);
     88         Layout layout = widget.getLayout();
     89 
     90         if (cap) {
     91             if (alt) {
     92                 return Selection.extendToLeftEdge(buffer, layout);
     93             } else {
     94                 return Selection.extendLeft(buffer, layout);
     95             }
     96         } else {
     97             if (alt) {
     98                 return Selection.moveToLeftEdge(buffer, layout);
     99             } else {
    100                 return Selection.moveLeft(buffer, layout);
    101             }
    102         }
    103     }
    104 
    105     private boolean right(TextView widget, Spannable buffer) {
    106         boolean cap = isCap(buffer);
    107         boolean alt = isAlt(buffer);
    108         Layout layout = widget.getLayout();
    109 
    110         if (cap) {
    111             if (alt) {
    112                 return Selection.extendToRightEdge(buffer, layout);
    113             } else {
    114                 return Selection.extendRight(buffer, layout);
    115             }
    116         } else {
    117             if (alt) {
    118                 return Selection.moveToRightEdge(buffer, layout);
    119             } else {
    120                 return Selection.moveRight(buffer, layout);
    121             }
    122         }
    123     }
    124 
    125     public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
    126         if (executeDown(widget, buffer, keyCode)) {
    127             MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
    128             MetaKeyKeyListener.resetLockedMeta(buffer);
    129             return true;
    130         }
    131 
    132         return false;
    133     }
    134 
    135     private boolean executeDown(TextView widget, Spannable buffer, int keyCode) {
    136         boolean handled = false;
    137 
    138         switch (keyCode) {
    139         case KeyEvent.KEYCODE_DPAD_UP:
    140             handled |= up(widget, buffer);
    141             break;
    142 
    143         case KeyEvent.KEYCODE_DPAD_DOWN:
    144             handled |= down(widget, buffer);
    145             break;
    146 
    147         case KeyEvent.KEYCODE_DPAD_LEFT:
    148             handled |= left(widget, buffer);
    149             break;
    150 
    151         case KeyEvent.KEYCODE_DPAD_RIGHT:
    152             handled |= right(widget, buffer);
    153             break;
    154 
    155         case KeyEvent.KEYCODE_DPAD_CENTER:
    156             if ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) &&
    157                 (widget.showContextMenu())) {
    158                     handled = true;
    159             }
    160         }
    161 
    162         if (handled) {
    163             MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
    164             MetaKeyKeyListener.resetLockedMeta(buffer);
    165         }
    166 
    167         return handled;
    168     }
    169 
    170     public boolean onKeyUp(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
    171         return false;
    172     }
    173 
    174     public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) {
    175         int code = event.getKeyCode();
    176         if (code != KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) {
    177             int repeat = event.getRepeatCount();
    178             boolean handled = false;
    179             while ((--repeat) > 0) {
    180                 handled |= executeDown(view, text, code);
    181             }
    182             return handled;
    183         }
    184         return false;
    185     }
    186 
    187     public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
    188         return false;
    189     }
    190 
    191     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
    192         int initialScrollX = -1, initialScrollY = -1;
    193         if (event.getAction() == MotionEvent.ACTION_UP) {
    194             initialScrollX = Touch.getInitialScrollX(widget, buffer);
    195             initialScrollY = Touch.getInitialScrollY(widget, buffer);
    196         }
    197 
    198         boolean handled = Touch.onTouchEvent(widget, buffer, event);
    199 
    200         if (widget.isFocused() && !widget.didTouchFocusSelect()) {
    201             if (event.getAction() == MotionEvent.ACTION_DOWN) {
    202               boolean cap = isCap(buffer);
    203               if (cap) {
    204                   int offset = widget.getOffset((int) event.getX(), (int) event.getY());
    205 
    206                   buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
    207 
    208                   // Disallow intercepting of the touch events, so that
    209                   // users can scroll and select at the same time.
    210                   // without this, users would get booted out of select
    211                   // mode once the view detected it needed to scroll.
    212                   widget.getParent().requestDisallowInterceptTouchEvent(true);
    213               }
    214             } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
    215                 boolean cap = isCap(buffer);
    216 
    217                 if (cap && handled) {
    218                     // Before selecting, make sure we've moved out of the "slop".
    219                     // handled will be true, if we're in select mode AND we're
    220                     // OUT of the slop
    221 
    222                     // Turn long press off while we're selecting. User needs to
    223                     // re-tap on the selection to enable long press
    224                     widget.cancelLongPress();
    225 
    226                     // Update selection as we're moving the selection area.
    227 
    228                     // Get the current touch position
    229                     int offset = widget.getOffset((int) event.getX(), (int) event.getY());
    230 
    231                     Selection.extendSelection(buffer, offset);
    232                     return true;
    233                 }
    234             } else if (event.getAction() == MotionEvent.ACTION_UP) {
    235                 // If we have scrolled, then the up shouldn't move the cursor,
    236                 // but we do need to make sure the cursor is still visible at
    237                 // the current scroll offset to avoid the scroll jumping later
    238                 // to show it.
    239                 if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) ||
    240                     (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) {
    241                     widget.moveCursorToVisibleOffset();
    242                     return true;
    243                 }
    244 
    245                 int offset = widget.getOffset((int) event.getX(), (int) event.getY());
    246                 if (isCap(buffer)) {
    247                     buffer.removeSpan(LAST_TAP_DOWN);
    248                     Selection.extendSelection(buffer, offset);
    249                 } else {
    250                     Selection.setSelection(buffer, offset);
    251                 }
    252 
    253                 MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
    254                 MetaKeyKeyListener.resetLockedMeta(buffer);
    255 
    256                 return true;
    257             }
    258         }
    259 
    260         return handled;
    261     }
    262 
    263     public boolean canSelectArbitrarily() {
    264         return true;
    265     }
    266 
    267     public void initialize(TextView widget, Spannable text) {
    268         Selection.setSelection(text, 0);
    269     }
    270 
    271     public void onTakeFocus(TextView view, Spannable text, int dir) {
    272         if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
    273             if (view.getLayout() == null) {
    274                 // This shouldn't be null, but do something sensible if it is.
    275                 Selection.setSelection(text, text.length());
    276             }
    277         } else {
    278             Selection.setSelection(text, text.length());
    279         }
    280     }
    281 
    282     public static MovementMethod getInstance() {
    283         if (sInstance == null) {
    284             sInstance = new ArrowKeyMovementMethod();
    285         }
    286 
    287         return sInstance;
    288     }
    289 
    290 
    291     private static final Object LAST_TAP_DOWN = new Object();
    292     private static ArrowKeyMovementMethod sInstance;
    293 }
    294