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