1 /* 2 * Copyright (C) 2011 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 com.android.inputmethod.latin; 18 19 import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; 20 import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; 21 22 import android.text.InputType; 23 import android.util.Log; 24 import android.view.inputmethod.EditorInfo; 25 26 import com.android.inputmethod.latin.utils.InputTypeUtils; 27 import com.android.inputmethod.latin.utils.StringUtils; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 32 /** 33 * Class to hold attributes of the input field. 34 */ 35 public final class InputAttributes { 36 private final String TAG = InputAttributes.class.getSimpleName(); 37 38 final public String mTargetApplicationPackageName; 39 final public boolean mInputTypeNoAutoCorrect; 40 final public boolean mIsPasswordField; 41 final public boolean mShouldShowSuggestions; 42 final public boolean mApplicationSpecifiedCompletionOn; 43 final public boolean mShouldInsertSpacesAutomatically; 44 final public boolean mShouldShowVoiceInputKey; 45 final public boolean mIsGeneralTextInput; 46 final private int mInputType; 47 final private EditorInfo mEditorInfo; 48 final private String mPackageNameForPrivateImeOptions; 49 InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode, final String packageNameForPrivateImeOptions)50 public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode, 51 final String packageNameForPrivateImeOptions) { 52 mEditorInfo = editorInfo; 53 mPackageNameForPrivateImeOptions = packageNameForPrivateImeOptions; 54 mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null; 55 final int inputType = null != editorInfo ? editorInfo.inputType : 0; 56 final int inputClass = inputType & InputType.TYPE_MASK_CLASS; 57 mInputType = inputType; 58 mIsPasswordField = InputTypeUtils.isPasswordInputType(inputType) 59 || InputTypeUtils.isVisiblePasswordInputType(inputType); 60 if (inputClass != InputType.TYPE_CLASS_TEXT) { 61 // If we are not looking at a TYPE_CLASS_TEXT field, the following strange 62 // cases may arise, so we do a couple sanity checks for them. If it's a 63 // TYPE_CLASS_TEXT field, these special cases cannot happen, by construction 64 // of the flags. 65 if (null == editorInfo) { 66 Log.w(TAG, "No editor info for this field. Bug?"); 67 } else if (InputType.TYPE_NULL == inputType) { 68 // TODO: We should honor TYPE_NULL specification. 69 Log.i(TAG, "InputType.TYPE_NULL is specified"); 70 } else if (inputClass == 0) { 71 // TODO: is this check still necessary? 72 Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" 73 + " imeOptions=0x%08x", inputType, editorInfo.imeOptions)); 74 } 75 mShouldShowSuggestions = false; 76 mInputTypeNoAutoCorrect = false; 77 mApplicationSpecifiedCompletionOn = false; 78 mShouldInsertSpacesAutomatically = false; 79 mShouldShowVoiceInputKey = false; 80 mIsGeneralTextInput = false; 81 return; 82 } 83 // inputClass == InputType.TYPE_CLASS_TEXT 84 final int variation = inputType & InputType.TYPE_MASK_VARIATION; 85 final boolean flagNoSuggestions = 86 0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 87 final boolean flagMultiLine = 88 0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE); 89 final boolean flagAutoCorrect = 90 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); 91 final boolean flagAutoComplete = 92 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); 93 94 // TODO: Have a helper method in InputTypeUtils 95 // Make sure that passwords are not displayed in {@link SuggestionStripView}. 96 final boolean shouldSuppressSuggestions = mIsPasswordField 97 || InputTypeUtils.isEmailVariation(variation) 98 || InputType.TYPE_TEXT_VARIATION_URI == variation 99 || InputType.TYPE_TEXT_VARIATION_FILTER == variation 100 || flagNoSuggestions 101 || flagAutoComplete; 102 mShouldShowSuggestions = !shouldSuppressSuggestions; 103 104 mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); 105 106 final boolean noMicrophone = mIsPasswordField 107 || InputTypeUtils.isEmailVariation(variation) 108 || InputType.TYPE_TEXT_VARIATION_URI == variation 109 || hasNoMicrophoneKeyOption(); 110 mShouldShowVoiceInputKey = !noMicrophone; 111 112 // If it's a browser edit field and auto correct is not ON explicitly, then 113 // disable auto correction, but keep suggestions on. 114 // If NO_SUGGESTIONS is set, don't do prediction. 115 // If it's not multiline and the autoCorrect flag is not set, then don't correct 116 mInputTypeNoAutoCorrect = 117 (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT && !flagAutoCorrect) 118 || flagNoSuggestions 119 || (!flagAutoCorrect && !flagMultiLine); 120 121 mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode; 122 123 // If we come here, inputClass is always TYPE_CLASS_TEXT 124 mIsGeneralTextInput = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS != variation 125 && InputType.TYPE_TEXT_VARIATION_PASSWORD != variation 126 && InputType.TYPE_TEXT_VARIATION_PHONETIC != variation 127 && InputType.TYPE_TEXT_VARIATION_URI != variation 128 && InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD != variation 129 && InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != variation 130 && InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD != variation; 131 } 132 isTypeNull()133 public boolean isTypeNull() { 134 return InputType.TYPE_NULL == mInputType; 135 } 136 isSameInputType(final EditorInfo editorInfo)137 public boolean isSameInputType(final EditorInfo editorInfo) { 138 return editorInfo.inputType == mInputType; 139 } 140 hasNoMicrophoneKeyOption()141 private boolean hasNoMicrophoneKeyOption() { 142 @SuppressWarnings("deprecation") 143 final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( 144 null, NO_MICROPHONE_COMPAT, mEditorInfo); 145 final boolean noMicrophone = InputAttributes.inPrivateImeOptions( 146 mPackageNameForPrivateImeOptions, NO_MICROPHONE, mEditorInfo); 147 return noMicrophone || deprecatedNoMicrophone; 148 } 149 150 @SuppressWarnings("unused") dumpFlags(final int inputType)151 private void dumpFlags(final int inputType) { 152 final int inputClass = inputType & InputType.TYPE_MASK_CLASS; 153 final String inputClassString = toInputClassString(inputClass); 154 final String variationString = toVariationString( 155 inputClass, inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); 156 final String flagsString = toFlagsString(inputType & InputType.TYPE_MASK_FLAGS); 157 Log.i(TAG, "Input class: " + inputClassString); 158 Log.i(TAG, "Variation: " + variationString); 159 Log.i(TAG, "Flags: " + flagsString); 160 } 161 toInputClassString(final int inputClass)162 private static String toInputClassString(final int inputClass) { 163 switch (inputClass) { 164 case InputType.TYPE_CLASS_TEXT: 165 return "TYPE_CLASS_TEXT"; 166 case InputType.TYPE_CLASS_PHONE: 167 return "TYPE_CLASS_PHONE"; 168 case InputType.TYPE_CLASS_NUMBER: 169 return "TYPE_CLASS_NUMBER"; 170 case InputType.TYPE_CLASS_DATETIME: 171 return "TYPE_CLASS_DATETIME"; 172 default: 173 return String.format("unknownInputClass<0x%08x>", inputClass); 174 } 175 } 176 toVariationString(final int inputClass, final int variation)177 private static String toVariationString(final int inputClass, final int variation) { 178 switch (inputClass) { 179 case InputType.TYPE_CLASS_TEXT: 180 return toTextVariationString(variation); 181 case InputType.TYPE_CLASS_NUMBER: 182 return toNumberVariationString(variation); 183 case InputType.TYPE_CLASS_DATETIME: 184 return toDatetimeVariationString(variation); 185 default: 186 return ""; 187 } 188 } 189 toTextVariationString(final int variation)190 private static String toTextVariationString(final int variation) { 191 switch (variation) { 192 case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS: 193 return " TYPE_TEXT_VARIATION_EMAIL_ADDRESS"; 194 case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT: 195 return "TYPE_TEXT_VARIATION_EMAIL_SUBJECT"; 196 case InputType.TYPE_TEXT_VARIATION_FILTER: 197 return "TYPE_TEXT_VARIATION_FILTER"; 198 case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE: 199 return "TYPE_TEXT_VARIATION_LONG_MESSAGE"; 200 case InputType.TYPE_TEXT_VARIATION_NORMAL: 201 return "TYPE_TEXT_VARIATION_NORMAL"; 202 case InputType.TYPE_TEXT_VARIATION_PASSWORD: 203 return "TYPE_TEXT_VARIATION_PASSWORD"; 204 case InputType.TYPE_TEXT_VARIATION_PERSON_NAME: 205 return "TYPE_TEXT_VARIATION_PERSON_NAME"; 206 case InputType.TYPE_TEXT_VARIATION_PHONETIC: 207 return "TYPE_TEXT_VARIATION_PHONETIC"; 208 case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS: 209 return "TYPE_TEXT_VARIATION_POSTAL_ADDRESS"; 210 case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE: 211 return "TYPE_TEXT_VARIATION_SHORT_MESSAGE"; 212 case InputType.TYPE_TEXT_VARIATION_URI: 213 return "TYPE_TEXT_VARIATION_URI"; 214 case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: 215 return "TYPE_TEXT_VARIATION_VISIBLE_PASSWORD"; 216 case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: 217 return "TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"; 218 case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS: 219 return "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"; 220 case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD: 221 return "TYPE_TEXT_VARIATION_WEB_PASSWORD"; 222 default: 223 return String.format("unknownVariation<0x%08x>", variation); 224 } 225 } 226 toNumberVariationString(final int variation)227 private static String toNumberVariationString(final int variation) { 228 switch (variation) { 229 case InputType.TYPE_NUMBER_VARIATION_NORMAL: 230 return "TYPE_NUMBER_VARIATION_NORMAL"; 231 case InputType.TYPE_NUMBER_VARIATION_PASSWORD: 232 return "TYPE_NUMBER_VARIATION_PASSWORD"; 233 default: 234 return String.format("unknownVariation<0x%08x>", variation); 235 } 236 } 237 toDatetimeVariationString(final int variation)238 private static String toDatetimeVariationString(final int variation) { 239 switch (variation) { 240 case InputType.TYPE_DATETIME_VARIATION_NORMAL: 241 return "TYPE_DATETIME_VARIATION_NORMAL"; 242 case InputType.TYPE_DATETIME_VARIATION_DATE: 243 return "TYPE_DATETIME_VARIATION_DATE"; 244 case InputType.TYPE_DATETIME_VARIATION_TIME: 245 return "TYPE_DATETIME_VARIATION_TIME"; 246 default: 247 return String.format("unknownVariation<0x%08x>", variation); 248 } 249 } 250 toFlagsString(final int flags)251 private static String toFlagsString(final int flags) { 252 final ArrayList<String> flagsArray = new ArrayList<>(); 253 if (0 != (flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)) 254 flagsArray.add("TYPE_TEXT_FLAG_NO_SUGGESTIONS"); 255 if (0 != (flags & InputType.TYPE_TEXT_FLAG_MULTI_LINE)) 256 flagsArray.add("TYPE_TEXT_FLAG_MULTI_LINE"); 257 if (0 != (flags & InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE)) 258 flagsArray.add("TYPE_TEXT_FLAG_IME_MULTI_LINE"); 259 if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_WORDS)) 260 flagsArray.add("TYPE_TEXT_FLAG_CAP_WORDS"); 261 if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)) 262 flagsArray.add("TYPE_TEXT_FLAG_CAP_SENTENCES"); 263 if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS)) 264 flagsArray.add("TYPE_TEXT_FLAG_CAP_CHARACTERS"); 265 if (0 != (flags & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT)) 266 flagsArray.add("TYPE_TEXT_FLAG_AUTO_CORRECT"); 267 if (0 != (flags & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE)) 268 flagsArray.add("TYPE_TEXT_FLAG_AUTO_COMPLETE"); 269 return flagsArray.isEmpty() ? "" : Arrays.toString(flagsArray.toArray()); 270 } 271 272 // Pretty print 273 @Override toString()274 public String toString() { 275 return String.format( 276 "%s: inputType=0x%08x%s%s%s%s%s targetApp=%s\n", getClass().getSimpleName(), 277 mInputType, 278 (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""), 279 (mIsPasswordField ? " password" : ""), 280 (mShouldShowSuggestions ? " shouldShowSuggestions" : ""), 281 (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""), 282 (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""), 283 mTargetApplicationPackageName); 284 } 285 inPrivateImeOptions(final String packageName, final String key, final EditorInfo editorInfo)286 public static boolean inPrivateImeOptions(final String packageName, final String key, 287 final EditorInfo editorInfo) { 288 if (editorInfo == null) return false; 289 final String findingKey = (packageName != null) ? packageName + "." + key : key; 290 return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions); 291 } 292 } 293