146b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown/*
246b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * Copyright (C) 2006 The Android Open Source Project
346b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown *
446b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
546b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * you may not use this file except in compliance with the License.
646b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * You may obtain a copy of the License at
746b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown *
846b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
946b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown *
1046b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * Unless required by applicable law or agreed to in writing, software
1146b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
1246b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1346b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * See the License for the specific language governing permissions and
1446b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * limitations under the License.
1546b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown */
1646b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown
1746b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brownpackage android.text.method;
1846b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown
1946b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brownimport android.view.KeyEvent;
20b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownimport android.view.View;
21b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownimport android.text.*;
22be1aa8250cee7819c49741e819e81659d1d03823Jeff Brownimport android.text.method.TextKeyListener.Capitalize;
23b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownimport android.widget.TextView;
249d3b1a424c5c61e24e9659d15fb353026a00d925Jeff Brown
259d3b1a424c5c61e24e9659d15fb353026a00d925Jeff Brown/**
269d3b1a424c5c61e24e9659d15fb353026a00d925Jeff Brown * Abstract base class for key listeners.
2746b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown *
2846b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * Provides a basic foundation for entering and editing text.
2946b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert
3046b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * characters as keys are pressed.
3146b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * <p></p>
3246b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * As for all implementations of {@link KeyListener}, this class is only concerned
3346b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * with hardware keyboards.  Software input methods have no obligation to trigger
3446b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown * the methods in this class.
3546b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown */
3646b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brownpublic abstract class BaseKeyListener extends MetaKeyKeyListener
37a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown        implements KeyListener {
38a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown    /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
39a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown
40a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown    /**
41a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown     * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_DEL} key in
42a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown     * a {@link TextView}.  If there is a selection, deletes the selection; otherwise,
43a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown     * deletes the character before the cursor, if any; ALT+DEL deletes everything on
44a47425a13c19f95057df78b8bb65bb25657e8753Jeff Brown     * the line the cursor is on.
4546b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown     *
4646b9ac0ae2162309774a7478cd9d4e578747bfc2Jeff Brown     * @return true if anything was deleted; false otherwise.
476d0fec2de3601821f4f44eeb7d7deedebb2b7117Jeff Brown     */
486d0fec2de3601821f4f44eeb7d7deedebb2b7117Jeff Brown    public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) {
496d0fec2de3601821f4f44eeb7d7deedebb2b7117Jeff Brown        return backspaceOrForwardDelete(view, content, keyCode, event, false);
50d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown    }
51d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown
52d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown    /**
53d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_FORWARD_DEL}
54d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     * key in a {@link TextView}.  If there is a selection, deletes the selection; otherwise,
55d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     * deletes the character before the cursor, if any; ALT+FORWARD_DEL deletes everything on
56d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     * the line the cursor is on.
57d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     *
58d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     * @return true if anything was deleted; false otherwise.
59d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown     */
60d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown    public boolean forwardDelete(View view, Editable content, int keyCode, KeyEvent event) {
61d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        return backspaceOrForwardDelete(view, content, keyCode, event, true);
62d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown    }
63d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown
64d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown    private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode,
6583d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            KeyEvent event, boolean isForwardDelete) {
6683d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        // Ensure the key event does not have modifiers except ALT or SHIFT.
67d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState()
68d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown                & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) {
6983d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            return false;
70d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        }
7183d616a9c7b9505153d258511eb5c16b552e268dJeff Brown
7283d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        // If there is a current selection, delete it.
73d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        if (deleteSelection(view, content)) {
74d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown            return true;
75d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        }
76d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown
77d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible.
78d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        if (getMetaState(content, META_ALT_ON, event) == 1) {
79d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown            if (deleteLine(view, content)) {
80d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown                return true;
81d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown            }
82d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        }
83d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown
84d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        // Delete a character.
8583d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        final int start = Selection.getSelectionEnd(content);
8683d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        final int end;
8783d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        if (isForwardDelete || event.isShiftPressed()
88d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown                || getMetaState(content, META_SHIFT_ON) == 1) {
89d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown            end = TextUtils.getOffsetAfter(content, start);
90d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        } else {
91d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown            end = TextUtils.getOffsetBefore(content, start);
92d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        }
9383d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        if (start != end) {
9483d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            content.delete(Math.min(start, end), Math.max(start, end));
9583d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            return true;
9683d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        }
9783d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        return false;
9883d616a9c7b9505153d258511eb5c16b552e268dJeff Brown    }
9983d616a9c7b9505153d258511eb5c16b552e268dJeff Brown
10083d616a9c7b9505153d258511eb5c16b552e268dJeff Brown    private boolean deleteSelection(View view, Editable content) {
10183d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        int selectionStart = Selection.getSelectionStart(content);
10283d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        int selectionEnd = Selection.getSelectionEnd(content);
10383d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        if (selectionEnd < selectionStart) {
10483d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            int temp = selectionEnd;
10583d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            selectionEnd = selectionStart;
10683d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            selectionStart = temp;
10783d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        }
10883d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        if (selectionStart != selectionEnd) {
10983d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            content.delete(selectionStart, selectionEnd);
11083d616a9c7b9505153d258511eb5c16b552e268dJeff Brown            return true;
11183d616a9c7b9505153d258511eb5c16b552e268dJeff Brown        }
112d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown        return false;
1138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
1149c3cda04d969912bc46184f2b326d1db95e0aba5Jeff Brown
115214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    private boolean deleteLine(View view, Editable content) {
116214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        if (view instanceof TextView) {
117214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            final Layout layout = ((TextView) view).getLayout();
118214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            if (layout != null) {
119214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown                final int line = layout.getLineForOffset(Selection.getSelectionStart(content));
120474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                final int start = layout.getLineStart(line);
121474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                final int end = layout.getLineEnd(line);
122474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                if (end != start) {
123474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                    content.delete(start, end);
124474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                    return true;
125474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                }
126474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown            }
127474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown        }
12865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        return false;
12965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    }
13065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
131daf4a127ba2af82a3fb477044b872719a0ab1827Jeff Brown    static int makeTextContentType(Capitalize caps, boolean autoText) {
132daf4a127ba2af82a3fb477044b872719a0ab1827Jeff Brown        int contentType = InputType.TYPE_CLASS_TEXT;
133daf4a127ba2af82a3fb477044b872719a0ab1827Jeff Brown        switch (caps) {
1346ec6f79e1ac1714e3b837796e99f07ff88f66601Jeff Brown            case CHARACTERS:
1356ec6f79e1ac1714e3b837796e99f07ff88f66601Jeff Brown                contentType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
1366ec6f79e1ac1714e3b837796e99f07ff88f66601Jeff Brown                break;
1375bbd4b4f5fc19302fa017ad6afee6eb2d489d91aJeff Brown            case WORDS:
1385bbd4b4f5fc19302fa017ad6afee6eb2d489d91aJeff Brown                contentType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
1395bbd4b4f5fc19302fa017ad6afee6eb2d489d91aJeff Brown                break;
140474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown            case SENTENCES:
141474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                contentType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
142474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                break;
143474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown        }
144214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        if (autoText) {
145214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            contentType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
146214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        }
147214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        return contentType;
148214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    }
149214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
150214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    public boolean onKeyDown(View view, Editable content,
151214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown                             int keyCode, KeyEvent event) {
152214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        boolean handled;
15319c97d46fb57f87ff45d9e6ea7122b4eb21ede8cJeff Brown        switch (keyCode) {
15419c97d46fb57f87ff45d9e6ea7122b4eb21ede8cJeff Brown            case KeyEvent.KEYCODE_DEL:
15519c97d46fb57f87ff45d9e6ea7122b4eb21ede8cJeff Brown                handled = backspace(view, content, keyCode, event);
15619c97d46fb57f87ff45d9e6ea7122b4eb21ede8cJeff Brown                break;
15719c97d46fb57f87ff45d9e6ea7122b4eb21ede8cJeff Brown            case KeyEvent.KEYCODE_FORWARD_DEL:
15819c97d46fb57f87ff45d9e6ea7122b4eb21ede8cJeff Brown                handled = forwardDelete(view, content, keyCode, event);
159474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                break;
160474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown            default:
161474dcb5c3ddff737c4ac9fc44a1f7be569605e5fJeff Brown                handled = false;
162214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown                break;
163214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        }
164214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
165214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        if (handled) {
166214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            adjustMetaAfterKeypress(content);
167214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        }
168214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
169214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        return super.onKeyDown(view, content, keyCode, event);
170214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    }
171214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
172214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    /**
173214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown     * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting
174214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown     * the event's text into the content.
175214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown     */
176214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    public boolean onKeyOther(View view, Editable content, KeyEvent event) {
177214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        if (event.getAction() != KeyEvent.ACTION_MULTIPLE
178214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown                || event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) {
179214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            // Not something we are interested in.
180214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            return false;
181214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        }
182214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
183214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        int selectionStart = Selection.getSelectionStart(content);
184214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        int selectionEnd = Selection.getSelectionEnd(content);
185214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        if (selectionEnd < selectionStart) {
186214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            int temp = selectionEnd;
187214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            selectionEnd = selectionStart;
188214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            selectionStart = temp;
189214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        }
190214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
191214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        CharSequence text = event.getCharacters();
192214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        if (text == null) {
193214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown            return false;
194214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        }
195bb3fcba0caf697f1d238a2cbefdf1efe06eded99Jeff Brown
196bb3fcba0caf697f1d238a2cbefdf1efe06eded99Jeff Brown        content.replace(selectionStart, selectionEnd, text);
197214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown        return true;
198214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown    }
199214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown}
200214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown
201214eaf48878bba00cbd5831871bcbd82632b6e34Jeff Brown