1 /*
2  * Copyright (C) 2006 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 
17 package android.widget;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.content.res.TypedArray;
22 import android.text.Editable;
23 import android.text.Selection;
24 import android.text.Spannable;
25 import android.text.TextUtils;
26 import android.text.method.ArrowKeyMovementMethod;
27 import android.text.method.MovementMethod;
28 import android.text.style.SpanUtils;
29 import android.util.AttributeSet;
30 import android.view.KeyEvent;
31 
32 /*
33  * This is supposed to be a *very* thin veneer over TextView.
34  * Do not make any changes here that do anything that a TextView
35  * with a key listener and a movement method wouldn't do!
36  */
37 
38 /**
39  * A user interface element for entering and modifying text.
40  * When you define an edit text widget, you must specify the
41  * {@link android.R.styleable#TextView_inputType}
42  * attribute. For example, for plain text input set inputType to "text":
43  * <p>
44  * <pre>
45  * &lt;EditText
46  *     android:id="@+id/plain_text_input"
47  *     android:layout_height="wrap_content"
48  *     android:layout_width="match_parent"
49  *     android:inputType="text"/&gt;</pre>
50  *
51  * Choosing the input type configures the keyboard type that is shown, acceptable characters,
52  * and appearance of the edit text.
53  * For example, if you want to accept a secret number, like a unique pin or serial number,
54  * you can set inputType to "numericPassword".
55  * An inputType of "numericPassword" results in an edit text that accepts numbers only,
56  * shows a numeric keyboard when focused, and masks the text that is entered for privacy.
57  * <p>
58  * See the <a href="{@docRoot}guide/topics/ui/controls/text.html">Text Fields</a>
59  * guide for examples of other
60  * {@link android.R.styleable#TextView_inputType} settings.
61  * </p>
62  * <p>You also can receive callbacks as a user changes text by
63  * adding a {@link android.text.TextWatcher} to the edit text.
64  * This is useful when you want to add auto-save functionality as changes are made,
65  * or validate the format of user input, for example.
66  * You add a text watcher using the {@link TextView#addTextChangedListener} method.
67  * </p>
68  * <p>
69  * This widget does not support auto-sizing text.
70  * <p>
71  * <b>XML attributes</b>
72  * <p>
73  * See {@link android.R.styleable#EditText EditText Attributes},
74  * {@link android.R.styleable#TextView TextView Attributes},
75  * {@link android.R.styleable#View View Attributes}
76  *
77  * @attr ref android.R.styleable#EditText_enableTextStylingShortcuts
78  */
79 public class EditText extends TextView {
80 
81     // True if the style shortcut is enabled.
82     private boolean mStyleShortcutsEnabled = false;
83 
84     private static final int ID_BOLD = android.R.id.bold;
85     private static final int ID_ITALIC = android.R.id.italic;
86     private static final int ID_UNDERLINE = android.R.id.underline;
87 
EditText(Context context)88     public EditText(Context context) {
89         this(context, null);
90     }
91 
EditText(Context context, AttributeSet attrs)92     public EditText(Context context, AttributeSet attrs) {
93         this(context, attrs, com.android.internal.R.attr.editTextStyle);
94     }
95 
EditText(Context context, AttributeSet attrs, int defStyleAttr)96     public EditText(Context context, AttributeSet attrs, int defStyleAttr) {
97         this(context, attrs, defStyleAttr, 0);
98     }
99 
EditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)100     public EditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
101         super(context, attrs, defStyleAttr, defStyleRes);
102 
103         final Resources.Theme theme = context.getTheme();
104         final TypedArray a = theme.obtainStyledAttributes(attrs,
105                 com.android.internal.R.styleable.EditText, defStyleAttr, defStyleRes);
106 
107         final int n = a.getIndexCount();
108         for (int i = 0; i < n; ++i) {
109             int attr = a.getIndex(i);
110             switch (attr) {
111                 case com.android.internal.R.styleable.EditText_enableTextStylingShortcuts:
112                     mStyleShortcutsEnabled = a.getBoolean(attr, false);
113                     break;
114             }
115         }
116     }
117 
118     @Override
getFreezesText()119     public boolean getFreezesText() {
120         return true;
121     }
122 
123     @Override
getDefaultEditable()124     protected boolean getDefaultEditable() {
125         return true;
126     }
127 
128     @Override
getDefaultMovementMethod()129     protected MovementMethod getDefaultMovementMethod() {
130         return ArrowKeyMovementMethod.getInstance();
131     }
132 
133     @Override
getText()134     public Editable getText() {
135         CharSequence text = super.getText();
136         // This can only happen during construction.
137         if (text == null) {
138             return null;
139         }
140         if (text instanceof Editable) {
141             return (Editable) text;
142         }
143         super.setText(text, BufferType.EDITABLE);
144         return (Editable) super.getText();
145     }
146 
147     @Override
setText(CharSequence text, BufferType type)148     public void setText(CharSequence text, BufferType type) {
149         super.setText(text, BufferType.EDITABLE);
150     }
151 
152     /**
153      * Convenience for {@link Selection#setSelection(Spannable, int, int)}.
154      */
setSelection(int start, int stop)155     public void setSelection(int start, int stop) {
156         Selection.setSelection(getText(), start, stop);
157     }
158 
159     /**
160      * Convenience for {@link Selection#setSelection(Spannable, int)}.
161      */
setSelection(int index)162     public void setSelection(int index) {
163         Selection.setSelection(getText(), index);
164     }
165 
166     /**
167      * Convenience for {@link Selection#selectAll}.
168      */
selectAll()169     public void selectAll() {
170         Selection.selectAll(getText());
171     }
172 
173     /**
174      * Convenience for {@link Selection#extendSelection}.
175      */
extendSelection(int index)176     public void extendSelection(int index) {
177         Selection.extendSelection(getText(), index);
178     }
179 
180     /**
181      * Causes words in the text that are longer than the view's width to be ellipsized instead of
182      * broken in the middle. {@link TextUtils.TruncateAt#MARQUEE
183      * TextUtils.TruncateAt#MARQUEE} is not supported.
184      *
185      * @param ellipsis Type of ellipsis to be applied.
186      * @throws IllegalArgumentException When the value of <code>ellipsis</code> parameter is
187      *      {@link TextUtils.TruncateAt#MARQUEE}.
188      * @see TextView#setEllipsize(TextUtils.TruncateAt)
189      */
190     @Override
setEllipsize(TextUtils.TruncateAt ellipsis)191     public void setEllipsize(TextUtils.TruncateAt ellipsis) {
192         if (ellipsis == TextUtils.TruncateAt.MARQUEE) {
193             throw new IllegalArgumentException("EditText cannot use the ellipsize mode "
194                     + "TextUtils.TruncateAt.MARQUEE");
195         }
196         super.setEllipsize(ellipsis);
197     }
198 
199     @Override
getAccessibilityClassName()200     public CharSequence getAccessibilityClassName() {
201         return EditText.class.getName();
202     }
203 
204     /** @hide */
205     @Override
supportsAutoSizeText()206     protected boolean supportsAutoSizeText() {
207         return false;
208     }
209 
210     @Override
onKeyShortcut(int keyCode, KeyEvent event)211     public boolean onKeyShortcut(int keyCode, KeyEvent event) {
212         if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
213             // Handle Ctrl-only shortcuts.
214             switch (keyCode) {
215                 case KeyEvent.KEYCODE_B:
216                     if (mStyleShortcutsEnabled && hasSelection()) {
217                         return onTextContextMenuItem(ID_BOLD);
218                     }
219                     break;
220                 case KeyEvent.KEYCODE_I:
221                     if (mStyleShortcutsEnabled && hasSelection()) {
222                         return onTextContextMenuItem(ID_ITALIC);
223                     }
224                     break;
225                 case KeyEvent.KEYCODE_U:
226                     if (mStyleShortcutsEnabled && hasSelection()) {
227                         return onTextContextMenuItem(ID_UNDERLINE);
228                     }
229                     break;
230             }
231         }
232         return super.onKeyShortcut(keyCode, event);
233     }
234 
235     @Override
onTextContextMenuItem(int id)236     public boolean onTextContextMenuItem(int id) {
237         // TODO: Move to switch-case once the resource ID is finalized.
238         if (id == ID_BOLD || id == ID_ITALIC || id == ID_UNDERLINE) {
239             return performStylingAction(id);
240         }
241         return super.onTextContextMenuItem(id);
242     }
243 
performStylingAction(int actionId)244     private boolean performStylingAction(int actionId) {
245         final int selectionStart = getSelectionStart();
246         final int selectionEnd = getSelectionEnd();
247         if (selectionStart < 0 || selectionEnd < 0) {
248             return false;  // There is no selection.
249         }
250         int min = Math.min(selectionStart, selectionEnd);
251         int max = Math.max(selectionStart, selectionEnd);
252 
253 
254         Spannable spannable = getText();
255         if (actionId == ID_BOLD) {
256             return SpanUtils.toggleBold(spannable, min, max);
257         } else if (actionId == ID_ITALIC) {
258             return SpanUtils.toggleItalic(spannable, min, max);
259         } else if (actionId == ID_UNDERLINE) {
260             return SpanUtils.toggleUnderline(spannable, min, max);
261         }
262 
263         return false;
264     }
265 
266     /**
267      * Enables styls shortcuts, e.g. Ctrl+B for making text bold.
268      *
269      * @param enabled true for enabled, false for disabled.
270      */
setStyleShortcutsEnabled(boolean enabled)271     public void setStyleShortcutsEnabled(boolean enabled) {
272         mStyleShortcutsEnabled = enabled;
273     }
274 
275     /**
276      * Return true if style shortcut is enabled, otherwise returns false.
277      * @return true if style shortcut is enabled, otherwise returns false.
278      */
isStyleShortcutEnabled()279     public boolean isStyleShortcutEnabled() {
280         return mStyleShortcutsEnabled;
281     }
282 }
283