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