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