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.view.KeyEvent; 20 import android.view.View; 21 import android.text.*; 22 import android.text.method.TextKeyListener.Capitalize; 23 import android.widget.TextView; 24 25 /** 26 * Abstract base class for key listeners. 27 * 28 * Provides a basic foundation for entering and editing text. 29 * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert 30 * characters as keys are pressed. 31 * <p></p> 32 * As for all implementations of {@link KeyListener}, this class is only concerned 33 * with hardware keyboards. Software input methods have no obligation to trigger 34 * the methods in this class. 35 */ 36 public abstract class BaseKeyListener extends MetaKeyKeyListener 37 implements KeyListener { 38 /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete(); 39 40 /** 41 * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_DEL} key in 42 * a {@link TextView}. If there is a selection, deletes the selection; otherwise, 43 * deletes the character before the cursor, if any; ALT+DEL deletes everything on 44 * the line the cursor is on. 45 * 46 * @return true if anything was deleted; false otherwise. 47 */ 48 public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) { 49 return backspaceOrForwardDelete(view, content, keyCode, event, false); 50 } 51 52 /** 53 * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_FORWARD_DEL} 54 * key in a {@link TextView}. If there is a selection, deletes the selection; otherwise, 55 * deletes the character before the cursor, if any; ALT+FORWARD_DEL deletes everything on 56 * the line the cursor is on. 57 * 58 * @return true if anything was deleted; false otherwise. 59 */ 60 public boolean forwardDelete(View view, Editable content, int keyCode, KeyEvent event) { 61 return backspaceOrForwardDelete(view, content, keyCode, event, true); 62 } 63 64 private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode, 65 KeyEvent event, boolean isForwardDelete) { 66 // Ensure the key event does not have modifiers except ALT or SHIFT. 67 if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState() 68 & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) { 69 return false; 70 } 71 72 // If there is a current selection, delete it. 73 if (deleteSelection(view, content)) { 74 return true; 75 } 76 77 // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. 78 if (getMetaState(content, META_ALT_ON, event) == 1) { 79 if (deleteLine(view, content)) { 80 return true; 81 } 82 } 83 84 // Delete a character. 85 final int start = Selection.getSelectionEnd(content); 86 final int end; 87 if (isForwardDelete || event.isShiftPressed() 88 || getMetaState(content, META_SHIFT_ON) == 1) { 89 end = TextUtils.getOffsetAfter(content, start); 90 } else { 91 end = TextUtils.getOffsetBefore(content, start); 92 } 93 if (start != end) { 94 content.delete(Math.min(start, end), Math.max(start, end)); 95 return true; 96 } 97 return false; 98 } 99 100 private boolean deleteSelection(View view, Editable content) { 101 int selectionStart = Selection.getSelectionStart(content); 102 int selectionEnd = Selection.getSelectionEnd(content); 103 if (selectionEnd < selectionStart) { 104 int temp = selectionEnd; 105 selectionEnd = selectionStart; 106 selectionStart = temp; 107 } 108 if (selectionStart != selectionEnd) { 109 content.delete(selectionStart, selectionEnd); 110 return true; 111 } 112 return false; 113 } 114 115 private boolean deleteLine(View view, Editable content) { 116 if (view instanceof TextView) { 117 final Layout layout = ((TextView) view).getLayout(); 118 if (layout != null) { 119 final int line = layout.getLineForOffset(Selection.getSelectionStart(content)); 120 final int start = layout.getLineStart(line); 121 final int end = layout.getLineEnd(line); 122 if (end != start) { 123 content.delete(start, end); 124 return true; 125 } 126 } 127 } 128 return false; 129 } 130 131 static int makeTextContentType(Capitalize caps, boolean autoText) { 132 int contentType = InputType.TYPE_CLASS_TEXT; 133 switch (caps) { 134 case CHARACTERS: 135 contentType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; 136 break; 137 case WORDS: 138 contentType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; 139 break; 140 case SENTENCES: 141 contentType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; 142 break; 143 } 144 if (autoText) { 145 contentType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; 146 } 147 return contentType; 148 } 149 150 public boolean onKeyDown(View view, Editable content, 151 int keyCode, KeyEvent event) { 152 boolean handled; 153 switch (keyCode) { 154 case KeyEvent.KEYCODE_DEL: 155 handled = backspace(view, content, keyCode, event); 156 break; 157 case KeyEvent.KEYCODE_FORWARD_DEL: 158 handled = forwardDelete(view, content, keyCode, event); 159 break; 160 default: 161 handled = false; 162 break; 163 } 164 165 if (handled) { 166 adjustMetaAfterKeypress(content); 167 } 168 169 return super.onKeyDown(view, content, keyCode, event); 170 } 171 172 /** 173 * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting 174 * the event's text into the content. 175 */ 176 public boolean onKeyOther(View view, Editable content, KeyEvent event) { 177 if (event.getAction() != KeyEvent.ACTION_MULTIPLE 178 || event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) { 179 // Not something we are interested in. 180 return false; 181 } 182 183 int selectionStart = Selection.getSelectionStart(content); 184 int selectionEnd = Selection.getSelectionEnd(content); 185 if (selectionEnd < selectionStart) { 186 int temp = selectionEnd; 187 selectionEnd = selectionStart; 188 selectionStart = temp; 189 } 190 191 CharSequence text = event.getCharacters(); 192 if (text == null) { 193 return false; 194 } 195 196 content.replace(selectionStart, selectionEnd, text); 197 return true; 198 } 199 } 200 201