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