1 /* 2 * Copyright (C) 2015 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 package android.support.car.input; 17 18 import android.content.Context; 19 import android.text.InputType; 20 import android.util.AttributeSet; 21 import android.view.ActionMode; 22 import android.view.KeyEvent; 23 import android.view.inputmethod.EditorInfo; 24 import android.view.inputmethod.InputConnection; 25 import android.view.inputmethod.InputConnectionWrapper; 26 import android.widget.EditText; 27 import android.widget.TextView; 28 29 /** 30 * A special EditText for use in-car. This EditText: 31 * <ul> 32 * <li>Disables selection</li> 33 * <li>Disables Cut/Copy/Paste</li> 34 * <li>Force-disables suggestions</li> 35 * </ul> 36 */ 37 public class CarRestrictedEditText extends EditText implements CarEditable { 38 39 private static final boolean SELECTION_CLAMPING_ENABLED = false; 40 41 private int mLastSelEnd = 0; 42 private int mLastSelStart = 0; 43 private boolean mCursorClamped; 44 45 private CarEditableListener mCarEditableListener; 46 private KeyListener mListener; 47 48 public interface KeyListener { onKeyDown(int keyCode)49 void onKeyDown(int keyCode); onKeyUp(int keyCode)50 void onKeyUp(int keyCode); onCommitText(String input)51 void onCommitText(String input); onCloseKeyboard()52 void onCloseKeyboard(); onDelete()53 void onDelete(); 54 } 55 CarRestrictedEditText(Context context, AttributeSet attrs)56 public CarRestrictedEditText(Context context, AttributeSet attrs) { 57 super(context, attrs); 58 setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 59 setTextIsSelectable(false); 60 setSelection(getText().length()); 61 mCursorClamped = true; 62 setOnEditorActionListener(new OnEditorActionListener() { 63 @Override 64 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 65 if (mListener != null && actionId == EditorInfo.IME_ACTION_DONE) { 66 mListener.onCloseKeyboard(); 67 } 68 // Return false because we don't want to hijack the default behavior. 69 return false; 70 } 71 }); 72 } 73 setKeyListener(KeyListener listener)74 public void setKeyListener(KeyListener listener) { 75 mListener = listener; 76 } 77 78 @SuppressWarnings("unused") 79 @Override onSelectionChanged(int selStart, int selEnd)80 protected void onSelectionChanged(int selStart, int selEnd) { 81 if (mCursorClamped && SELECTION_CLAMPING_ENABLED) { 82 setSelection(mLastSelStart, mLastSelEnd); 83 return; 84 } 85 if (mCarEditableListener != null) { 86 mCarEditableListener.onUpdateSelection(mLastSelStart, mLastSelEnd, selStart, selEnd); 87 } 88 mLastSelStart = selStart; 89 mLastSelEnd = selEnd; 90 } 91 92 @Override startActionMode(ActionMode.Callback callback)93 public ActionMode startActionMode(ActionMode.Callback callback) { 94 return null; 95 } 96 97 @Override setCarEditableListener(CarEditableListener listener)98 public void setCarEditableListener(CarEditableListener listener) { 99 mCarEditableListener = listener; 100 } 101 102 @Override setInputEnabled(boolean enabled)103 public void setInputEnabled(boolean enabled) { 104 mCursorClamped = !enabled; 105 } 106 107 @Override onCreateInputConnection(EditorInfo outAttrs)108 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 109 InputConnection inputConnection = super.onCreateInputConnection(outAttrs); 110 return new InputConnectionWrapper(inputConnection, false) { 111 @Override 112 public boolean sendKeyEvent(android.view.KeyEvent event) { 113 if (mListener != null) { 114 if (event.getAction() == KeyEvent.ACTION_DOWN) { 115 mListener.onKeyDown(event.getKeyCode()); 116 } else if (event.getAction() == KeyEvent.ACTION_UP) { 117 mListener.onKeyUp(event.getKeyCode()); 118 119 // InputMethodService#sendKeyChar doesn't call 120 // InputConnection#commitText for digit chars. 121 // TODO: fix projected IME to be in coherence with system IME. 122 char unicodeChar = (char) event.getUnicodeChar(); 123 if (Character.isDigit(unicodeChar)) { 124 commitText(String.valueOf(unicodeChar), 1); 125 } 126 } 127 return true; 128 } else { 129 return super.sendKeyEvent(event); 130 } 131 } 132 133 @Override 134 public boolean commitText(java.lang.CharSequence charSequence, int i) { 135 if (mListener != null) { 136 mListener.onCommitText(charSequence.toString()); 137 return true; 138 } 139 return super.commitText(charSequence, i); 140 } 141 142 @Override 143 public boolean deleteSurroundingText(int i, int i1) { 144 if (mListener != null) { 145 mListener.onDelete(); 146 return true; 147 } 148 return super.deleteSurroundingText(i, i1); 149 } 150 }; 151 } 152 } 153