BaseKeyListener.java revision 405bc51c5dc73846a4abdc325cd234eb2d37469f
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
17package android.text.method;
18
19import android.view.KeyEvent;
20import android.view.View;
21import android.text.*;
22import android.text.method.TextKeyListener.Capitalize;
23import 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 */
36public 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 (event.isAltPressed() || getMetaState(content, META_ALT_ON) == 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