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 com.android.launcher3; 17 18 import android.content.Context; 19 import android.graphics.Rect; 20 import android.text.TextUtils; 21 import android.util.AttributeSet; 22 import android.util.Log; 23 import android.view.DragEvent; 24 import android.view.KeyEvent; 25 import android.view.inputmethod.InputMethodManager; 26 import android.widget.EditText; 27 28 import com.android.launcher3.views.ActivityContext; 29 30 import java.util.HashSet; 31 import java.util.Set; 32 33 34 /** 35 * The edit text that reports back when the back key has been pressed. 36 * Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion 37 */ 38 public class ExtendedEditText extends EditText { 39 private static final String TAG = "ExtendedEditText"; 40 41 private final Set<OnFocusChangeListener> mOnFocusChangeListeners = new HashSet<>(); 42 43 private boolean mForceDisableSuggestions = false; 44 45 /** 46 * Implemented by listeners of the back key. 47 */ 48 public interface OnBackKeyListener { onBackKey()49 boolean onBackKey(); 50 } 51 52 private OnBackKeyListener mBackKeyListener; 53 ExtendedEditText(Context context)54 public ExtendedEditText(Context context) { 55 // ctor chaining breaks the touch handling 56 super(context); 57 } 58 ExtendedEditText(Context context, AttributeSet attrs)59 public ExtendedEditText(Context context, AttributeSet attrs) { 60 // ctor chaining breaks the touch handling 61 super(context, attrs); 62 } 63 ExtendedEditText(Context context, AttributeSet attrs, int defStyleAttr)64 public ExtendedEditText(Context context, AttributeSet attrs, int defStyleAttr) { 65 super(context, attrs, defStyleAttr); 66 } 67 setOnBackKeyListener(OnBackKeyListener listener)68 public void setOnBackKeyListener(OnBackKeyListener listener) { 69 mBackKeyListener = listener; 70 } 71 72 @Override onKeyPreIme(int keyCode, KeyEvent event)73 public boolean onKeyPreIme(int keyCode, KeyEvent event) { 74 // If this is a back key, propagate the key back to the listener 75 if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP 76 && mBackKeyListener != null) { 77 return mBackKeyListener.onBackKey(); 78 } 79 return super.onKeyPreIme(keyCode, event); 80 } 81 82 @Override onDragEvent(DragEvent event)83 public boolean onDragEvent(DragEvent event) { 84 // We don't want this view to interfere with Launcher own drag and drop. 85 return false; 86 } 87 88 /** 89 * Synchronously shows the soft input method. 90 * 91 * @return true if the keyboard is shown correctly and focus is given to this view. 92 */ showKeyboard()93 public boolean showKeyboard() { 94 return requestFocus() && showSoftInputInternal(); 95 } 96 97 /** 98 * Requests the framework to show the keyboard in order to ensure that an already registered 99 * controlled keyboard animation is triggered correctly. 100 * Must NEVER be called in any other case than to trigger a pre-registered controlled animation. 101 */ requestShowKeyboardForControlledAnimation()102 public void requestShowKeyboardForControlledAnimation() { 103 // We don't log the keyboard state, as that must happen only after the controlled animation 104 // has completed. 105 // We also must not request focus, as this triggers unwanted side effects. 106 showSoftInputInternal(); 107 } 108 hideKeyboard()109 public void hideKeyboard() { 110 hideKeyboard(/* clearFocus= */ true); 111 } 112 hideKeyboard(boolean clearFocus)113 public void hideKeyboard(boolean clearFocus) { 114 ActivityContext.lookupContext(getContext()).hideKeyboard(); 115 if (clearFocus) { 116 clearFocus(); 117 } 118 } 119 showSoftInputInternal()120 private boolean showSoftInputInternal() { 121 boolean result = false; 122 InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); 123 if (imm != null) { 124 result = imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT); 125 } else { 126 Log.w(TAG, "Failed to retrieve InputMethodManager from the system."); 127 } 128 return result; 129 } 130 dispatchBackKey()131 public void dispatchBackKey() { 132 hideKeyboard(); 133 if (mBackKeyListener != null) { 134 mBackKeyListener.onBackKey(); 135 } 136 } 137 138 /** 139 * Set to true when you want isSuggestionsEnabled to return false. 140 * Use this to disable the red underlines that appear under typos when suggestions is enabled. 141 */ forceDisableSuggestions(boolean forceDisableSuggestions)142 public void forceDisableSuggestions(boolean forceDisableSuggestions) { 143 mForceDisableSuggestions = forceDisableSuggestions; 144 } 145 146 @Override isSuggestionsEnabled()147 public boolean isSuggestionsEnabled() { 148 return !mForceDisableSuggestions && super.isSuggestionsEnabled(); 149 } 150 reset()151 public void reset() { 152 if (!TextUtils.isEmpty(getText())) { 153 setText(""); 154 } 155 } 156 157 @Override setText(CharSequence text, BufferType type)158 public void setText(CharSequence text, BufferType type) { 159 super.setText(text, type); 160 // With hardware keyboard, there is a possibility that the user types before edit 161 // text is visible during the transition. 162 // So move the cursor to the end of the text. 163 setSelection(getText().length()); 164 } 165 166 /** 167 * This method should be preferred to {@link #setOnFocusChangeListener(OnFocusChangeListener)}, 168 * as it allows for multiple listeners from different sources. 169 */ addOnFocusChangeListener(OnFocusChangeListener listener)170 public void addOnFocusChangeListener(OnFocusChangeListener listener) { 171 mOnFocusChangeListeners.add(listener); 172 } 173 174 /** 175 * Removes the given listener from the set of registered focus listeners, or does nothing if it 176 * wasn't registered in the first place. 177 */ removeOnFocusChangeListener(OnFocusChangeListener listener)178 public void removeOnFocusChangeListener(OnFocusChangeListener listener) { 179 mOnFocusChangeListeners.remove(listener); 180 } 181 182 @Override onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)183 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 184 super.onFocusChanged(focused, direction, previouslyFocusedRect); 185 for (OnFocusChangeListener listener : mOnFocusChangeListeners) { 186 listener.onFocusChange(this, focused); 187 } 188 } 189 190 /** 191 * Save the input, suggestion, hint states when it's on focus, and set to unfocused states. 192 */ saveFocusedStateAndUpdateToUnfocusedState()193 public void saveFocusedStateAndUpdateToUnfocusedState() {} 194 195 /** 196 * Restore to the previous saved focused state. 197 */ restoreToFocusedState()198 public void restoreToFocusedState() {} 199 } 200