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