BaseKeyListener.java revision d17247164b3c4e8a41cbe1dd283ef2442ad76229
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 */
32public 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