19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text.method; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.*; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener.Capitalize; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.TextView; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 256b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown/** 266b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Abstract base class for key listeners. 276b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * 286b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Provides a basic foundation for entering and editing text. 296b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert 306b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * characters as keys are pressed. 31405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * <p></p> 32405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * As for all implementations of {@link KeyListener}, this class is only concerned 33405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * with hardware keyboards. Software input methods have no obligation to trigger 34405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * the methods in this class. 356b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown */ 366b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brownpublic abstract class BaseKeyListener extends MetaKeyKeyListener 376b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown implements KeyListener { 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete(); 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_DEL} key in 4214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * a {@link TextView}. If there is a selection, deletes the selection; otherwise, 4314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * deletes the character before the cursor, if any; ALT+DEL deletes everything on 4414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * the line the cursor is on. 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 466b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * @return true if anything was deleted; false otherwise. 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) { 49d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown return backspaceOrForwardDelete(view, content, keyCode, event, false); 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown /** 5314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_FORWARD_DEL} 5414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * key in a {@link TextView}. If there is a selection, deletes the selection; otherwise, 5514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * deletes the character before the cursor, if any; ALT+FORWARD_DEL deletes everything on 5614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * the line the cursor is on. 5714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * 5814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * @return true if anything was deleted; false otherwise. 5914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown */ 6014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown public boolean forwardDelete(View view, Editable content, int keyCode, KeyEvent event) { 61d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown return backspaceOrForwardDelete(view, content, keyCode, event, true); 62d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } 63d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown 64d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode, 65d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown KeyEvent event, boolean isForwardDelete) { 66d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown // Ensure the key event does not have modifiers except ALT or SHIFT. 67d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState() 68d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) { 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 72d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown // If there is a current selection, delete it. 7314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (deleteSelection(view, content)) { 7414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 77d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. 7814f10e5d5f51bc54ca2a45ee62d3cfb6debd3af0Raph Levien if (getMetaState(content, META_ALT_ON, event) == 1) { 79d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown if (deleteLine(view, content)) { 80d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown return true; 81d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } 8214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 84d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown // Delete a character. 8514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int start = Selection.getSelectionEnd(content); 86d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown final int end; 87d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown if (isForwardDelete || event.isShiftPressed() 88d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown || getMetaState(content, META_SHIFT_ON) == 1) { 89d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown end = TextUtils.getOffsetAfter(content, start); 90d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } else { 91d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown end = TextUtils.getOffsetBefore(content, start); 92d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } 9314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (start != end) { 9414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.delete(Math.min(start, end), Math.max(start, end)); 9514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return false; 9814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown private boolean deleteSelection(View view, Editable content) { 10114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionStart = Selection.getSelectionStart(content); 10214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionEnd = Selection.getSelectionEnd(content); 10314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (selectionEnd < selectionStart) { 10414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int temp = selectionEnd; 10514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionEnd = selectionStart; 10614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionStart = temp; 10714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 10814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (selectionStart != selectionEnd) { 10914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.delete(selectionStart, selectionEnd); 11014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return false; 11314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown private boolean deleteLine(View view, Editable content) { 11614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (view instanceof TextView) { 11714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final Layout layout = ((TextView) view).getLayout(); 11814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (layout != null) { 11914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int line = layout.getLineForOffset(Selection.getSelectionStart(content)); 12014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int start = layout.getLineStart(line); 12114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int end = layout.getLineEnd(line); 12214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (end != start) { 12314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.delete(start, end); 12414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 12514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 12614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 12714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 12814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return false; 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static int makeTextContentType(Capitalize caps, boolean autoText) { 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int contentType = InputType.TYPE_CLASS_TEXT; 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (caps) { 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case CHARACTERS: 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case WORDS: 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case SENTENCES: 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (autoText) { 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return contentType; 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyDown(View view, Editable content, 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int keyCode, KeyEvent event) { 15214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown boolean handled; 15314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown switch (keyCode) { 15414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown case KeyEvent.KEYCODE_DEL: 15514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown handled = backspace(view, content, keyCode, event); 15614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown break; 15714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown case KeyEvent.KEYCODE_FORWARD_DEL: 15814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown handled = forwardDelete(view, content, keyCode, event); 15914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown break; 16014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown default: 16114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown handled = false; 16214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown break; 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 16514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (handled) { 16614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown adjustMetaAfterKeypress(content); 16714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 16814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return super.onKeyDown(view, content, keyCode, event); 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the event's text into the content. 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyOther(View view, Editable content, KeyEvent event) { 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (event.getAction() != KeyEvent.ACTION_MULTIPLE 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project || event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) { 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Not something we are interested in. 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionStart = Selection.getSelectionStart(content); 18414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionEnd = Selection.getSelectionEnd(content); 18514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (selectionEnd < selectionStart) { 18614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int temp = selectionEnd; 18714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionEnd = selectionStart; 18814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionStart = temp; 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = event.getCharacters(); 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (text == null) { 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 19614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.replace(selectionStart, selectionEnd, text); 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 201