15e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen/*
25e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * Copyright (C) 2015 The Android Open Source Project
35e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *
45e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * Licensed under the Apache License, Version 2.0 (the "License");
55e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * you may not use this file except in compliance with the License.
65e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * You may obtain a copy of the License at
75e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *
85e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *      http://www.apache.org/licenses/LICENSE-2.0
95e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *
105e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * Unless required by applicable law or agreed to in writing, software
115e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * distributed under the License is distributed on an "AS IS" BASIS,
125e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * See the License for the specific language governing permissions and
145e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * limitations under the License.
155e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen */
165e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenpackage android.support.car.input;
175e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
185e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.content.Context;
195e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.text.InputType;
205e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.util.AttributeSet;
215e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.view.ActionMode;
225e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.view.KeyEvent;
235e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.view.inputmethod.EditorInfo;
245e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.view.inputmethod.InputConnection;
255e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.view.inputmethod.InputConnectionWrapper;
265e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.widget.EditText;
275e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenimport android.widget.TextView;
285e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
295e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen/**
305e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * A special EditText for use in-car. This EditText:
315e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * <ul>
325e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *     <li>Disables selection</li>
335e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *     <li>Disables Cut/Copy/Paste</li>
345e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen *     <li>Force-disables suggestions</li>
355e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen * </ul>
365e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen */
375e36c880189207d5956feea2474ea4620ae1f2b7Yao Chenpublic class CarRestrictedEditText extends EditText implements CarEditable {
385e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
395e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    private static final boolean SELECTION_CLAMPING_ENABLED = false;
405e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
415e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    private int mLastSelEnd = 0;
425e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    private int mLastSelStart = 0;
435e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    private boolean mCursorClamped;
445e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
455e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    private CarEditableListener mCarEditableListener;
465e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    private KeyListener mListener;
475e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
485e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public interface KeyListener {
499f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev        void onKeyDown(int keyCode);
509f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev        void onKeyUp(int keyCode);
515e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        void onCommitText(String input);
525e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        void onCloseKeyboard();
535e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        void onDelete();
545e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
555e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
565e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public CarRestrictedEditText(Context context, AttributeSet attrs) {
575e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        super(context, attrs);
585e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
595e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        setTextIsSelectable(false);
605e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        setSelection(getText().length());
615e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        mCursorClamped = true;
625e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        setOnEditorActionListener(new OnEditorActionListener() {
635e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            @Override
645e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
655e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                if (mListener != null && actionId == EditorInfo.IME_ACTION_DONE) {
665e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    mListener.onCloseKeyboard();
675e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                }
685e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                // Return false because we don't want to hijack the default behavior.
695e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                return false;
705e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            }
715e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        });
725e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
735e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
745e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public void setKeyListener(KeyListener listener) {
755e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        mListener = listener;
765e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
775e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
785e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    @SuppressWarnings("unused")
795e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    @Override
805e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    protected void onSelectionChanged(int selStart, int selEnd) {
815e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        if (mCursorClamped && SELECTION_CLAMPING_ENABLED) {
825e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            setSelection(mLastSelStart, mLastSelEnd);
835e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            return;
845e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        }
855e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        if (mCarEditableListener != null) {
865e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            mCarEditableListener.onUpdateSelection(mLastSelStart, mLastSelEnd, selStart, selEnd);
875e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        }
885e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        mLastSelStart = selStart;
895e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        mLastSelEnd = selEnd;
905e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
915e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
925e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    @Override
935e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public ActionMode startActionMode(ActionMode.Callback callback) {
945e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        return null;
955e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
965e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
975e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    @Override
985e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public void setCarEditableListener(CarEditableListener listener) {
995e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        mCarEditableListener = listener;
1005e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
1015e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
1025e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    @Override
1035e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public void setInputEnabled(boolean enabled) {
1045e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        mCursorClamped = !enabled;
1055e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
1065e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
1075e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    @Override
1085e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1095e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
1105e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        return new InputConnectionWrapper(inputConnection, false) {
1115e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            @Override
1125e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            public boolean sendKeyEvent(android.view.KeyEvent event) {
1135e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                if (mListener != null) {
1145e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    if (event.getAction() == KeyEvent.ACTION_DOWN) {
1159f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        mListener.onKeyDown(event.getKeyCode());
1165e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    } else if (event.getAction() == KeyEvent.ACTION_UP) {
1179f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        mListener.onKeyUp(event.getKeyCode());
1189f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev
1199f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        // InputMethodService#sendKeyChar doesn't call
1209f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        // InputConnection#commitText for digit chars.
1219f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        // TODO: fix projected IME to be in coherence with system IME.
1229f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        char unicodeChar = (char) event.getUnicodeChar();
1239f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        if (Character.isDigit(unicodeChar)) {
1249f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                            commitText(String.valueOf(unicodeChar), 1);
1259f3ddde81e93beb8591545db9e892b257f65efdePavel Maltsev                        }
1265e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    }
1275e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    return true;
1285e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                } else {
1295e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    return super.sendKeyEvent(event);
1305e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                }
1315e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            }
1325e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
1335e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            @Override
1345e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            public boolean commitText(java.lang.CharSequence charSequence, int i) {
1355e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                if (mListener != null) {
1365e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    mListener.onCommitText(charSequence.toString());
1375e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    return true;
1385e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                }
1395e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                return super.commitText(charSequence, i);
1405e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            }
1415e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen
1425e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            @Override
1435e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            public boolean deleteSurroundingText(int i, int i1) {
1445e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                if (mListener != null) {
1455e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    mListener.onDelete();
1465e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                    return true;
1475e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                }
1485e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen                return super.deleteSurroundingText(i, i1);
1495e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen            }
1505e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen        };
1515e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen    }
1525e36c880189207d5956feea2474ea4620ae1f2b7Yao Chen}
153