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