aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod
diff options
context:
space:
mode:
authorAmin Bandali <bandali@kelar.org>2024-12-16 21:45:41 -0500
committerAmin Bandali <bandali@kelar.org>2025-01-11 14:17:35 -0500
commite9a0e66716dab4dd3184d009d8920de1961efdfa (patch)
tree02dcc096643d74645bf28459c2834c3d4a2ad7f2 /java/src/com/android/inputmethod
parentfb3b9360d70596d7e921de8bf7d3ca99564a077e (diff)
downloadlatinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.gz
latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.xz
latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.zip
Rename to Kelar Keyboard (org.kelar.inputmethod.latin)
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java67
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java266
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java365
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java326
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java339
-rw-r--r--java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java293
-rw-r--r--java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java120
-rw-r--r--java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java46
-rw-r--r--java/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java30
-rw-r--r--java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java60
-rw-r--r--java/src/com/android/inputmethod/compat/BuildCompatUtils.java36
-rw-r--r--java/src/com/android/inputmethod/compat/CharacterCompat.java47
-rw-r--r--java/src/com/android/inputmethod/compat/CompatUtils.java218
-rw-r--r--java/src/com/android/inputmethod/compat/ConnectivityManagerCompatUtils.java36
-rw-r--r--java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java185
-rw-r--r--java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java98
-rw-r--r--java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java64
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java52
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java37
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java103
-rw-r--r--java/src/com/android/inputmethod/compat/IntentCompatUtils.java35
-rw-r--r--java/src/com/android/inputmethod/compat/LocaleListCompatUtils.java40
-rw-r--r--java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java218
-rw-r--r--java/src/com/android/inputmethod/compat/LooperCompatUtils.java42
-rw-r--r--java/src/com/android/inputmethod/compat/NotificationCompatUtils.java83
-rw-r--r--java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java36
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java121
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java47
-rw-r--r--java/src/com/android/inputmethod/compat/TextInfoCompatUtils.java67
-rw-r--r--java/src/com/android/inputmethod/compat/TextViewCompatUtils.java44
-rw-r--r--java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java49
-rw-r--r--java/src/com/android/inputmethod/compat/UserManagerCompatUtils.java80
-rw-r--r--java/src/com/android/inputmethod/compat/ViewCompatUtils.java70
-rw-r--r--java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java43
-rw-r--r--java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java72
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/ActionBatch.java625
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/AssetFileAddress.java66
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/BadFormatException.java30
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java170
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java40
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/CompletedDownloadInfo.java36
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java173
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java85
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java72
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java541
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryService.java280
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java54
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java438
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java29
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java112
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java86
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadRecord.java37
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/EventHandler.java46
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java35
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java46
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java1155
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java173
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataParser.java114
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java29
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/PrivateLog.java102
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/ProblemReporter.java24
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java1083
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java135
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/WordListPreference.java310
-rw-r--r--java/src/com/android/inputmethod/event/Combiner.java51
-rw-r--r--java/src/com/android/inputmethod/event/CombinerChain.java137
-rw-r--r--java/src/com/android/inputmethod/event/DeadKeyCombiner.java303
-rw-r--r--java/src/com/android/inputmethod/event/Event.java319
-rw-r--r--java/src/com/android/inputmethod/event/EventDecoder.java24
-rw-r--r--java/src/com/android/inputmethod/event/HardwareEventDecoder.java26
-rw-r--r--java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java81
-rw-r--r--java/src/com/android/inputmethod/event/InputTransaction.java116
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java1022
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java116
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java261
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java132
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java271
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayout.java124
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java508
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java508
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardTheme.java215
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java590
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java895
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java55
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java369
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java320
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java136
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java1198
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java405
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/DynamicGridKeyboard.java264
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java470
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiCategoryPageIndicatorView.java70
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiLayoutParams.java94
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java233
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java149
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java486
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java84
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java131
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java181
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java115
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java107
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java88
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java79
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureEnabler.java54
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java184
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingParams.java58
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java197
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionParams.java109
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java334
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingParams.java79
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java276
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java174
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java161
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java167
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java209
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java188
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java139
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java258
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java52
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java230
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java148
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java889
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java83
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java167
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java193
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java187
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java711
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java151
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java4198
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java166
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java83
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java355
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java115
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java238
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java113
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java69
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java106
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java102
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java234
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java133
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java97
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TypingTimeRecorder.java72
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/UniqueKeysCache.java81
-rw-r--r--java/src/com/android/inputmethod/latin/AssetFileAddress.java70
-rw-r--r--java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java134
-rw-r--r--java/src/com/android/inputmethod/latin/BackupAgent.java57
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java669
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java569
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java291
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java176
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsContentObserver.java136
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionaryConstants.java52
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionaryUtils.java55
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsManager.java245
-rw-r--r--java/src/com/android/inputmethod/latin/DicTraverseSession.java98
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java216
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java140
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java50
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java176
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java736
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java106
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitatorProvider.java26
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java161
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java141
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryStats.java103
-rw-r--r--java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java207
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java757
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java303
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java252
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java93
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java2033
-rw-r--r--java/src/com/android/inputmethod/latin/NgramContext.java291
-rw-r--r--java/src/com/android/inputmethod/latin/PunctuationSuggestions.java124
-rw-r--r--java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java127
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java1033
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodManager.java612
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java250
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java434
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java448
-rw-r--r--java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java159
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java216
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java481
-rw-r--r--java/src/com/android/inputmethod/latin/WordListInfo.java31
-rw-r--r--java/src/com/android/inputmethod/latin/about/AboutPreferences.java28
-rw-r--r--java/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java75
-rw-r--r--java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java81
-rw-r--r--java/src/com/android/inputmethod/latin/accounts/AuthUtils.java67
-rw-r--r--java/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java47
-rw-r--r--java/src/com/android/inputmethod/latin/define/DebugFlags.java31
-rw-r--r--java/src/com/android/inputmethod/latin/define/DecoderSpecificConstants.java38
-rw-r--r--java/src/com/android/inputmethod/latin/define/JniLibName.java25
-rw-r--r--java/src/com/android/inputmethod/latin/define/ProductionFlags.java58
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java2353
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java221
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java40
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/SpaceState.java54
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java91
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java310
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/NgramProperty.java42
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java87
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java26
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/WeightedString.java62
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/WordProperty.java201
-rw-r--r--java/src/com/android/inputmethod/latin/network/AuthException.java35
-rw-r--r--java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java97
-rw-r--r--java/src/com/android/inputmethod/latin/network/HttpException.java46
-rw-r--r--java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java229
-rw-r--r--java/src/com/android/inputmethod/latin/permissions/PermissionsActivity.java97
-rw-r--r--java/src/com/android/inputmethod/latin/permissions/PermissionsManager.java91
-rw-r--r--java/src/com/android/inputmethod/latin/permissions/PermissionsUtil.java93
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/AccountUtils.java66
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java108
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java136
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java508
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java57
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java264
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java46
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java152
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java341
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java318
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettings.java53
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java288
-rw-r--r--java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java38
-rw-r--r--java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java61
-rw-r--r--java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java104
-rw-r--r--java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java97
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SeekBarDialogPreference.java147
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java458
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsActivity.java88
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java101
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java452
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValuesForSuggestion.java25
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java155
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java134
-rw-r--r--java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java55
-rw-r--r--java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java112
-rw-r--r--java/src/com/android/inputmethod/latin/settings/TwoStatePreferenceHelper.java82
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupActivity.java36
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java123
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java62
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java513
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java244
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java225
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java25
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java390
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java197
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java61
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java90
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java268
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java117
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java650
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java491
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java27
-rw-r--r--java/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java69
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java286
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java179
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java165
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java36
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java352
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java42
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java238
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java83
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java72
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java62
-rw-r--r--java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java128
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java357
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java109
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CompletionInfoUtils.java43
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java264
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java115
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DialogUtils.java34
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DictionaryHeaderUtils.java31
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java613
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java152
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FeedbackUtils.java38
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FileTransforms.java38
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FragmentUtils.java64
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java140
-rw-r--r--java/src/com/android/inputmethod/latin/utils/InputTypeUtils.java117
-rw-r--r--java/src/com/android/inputmethod/latin/utils/IntentUtils.java45
-rw-r--r--java/src/com/android/inputmethod/latin/utils/JniUtils.java41
-rw-r--r--java/src/com/android/inputmethod/latin/utils/JsonUtils.java103
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LanguageOnSpacebarUtils.java92
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java43
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java43
-rw-r--r--java/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java39
-rw-r--r--java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java113
-rw-r--r--java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java221
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ResourceUtils.java320
-rw-r--r--java/src/com/android/inputmethod/latin/utils/RunInLocale.java53
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ScriptUtils.java195
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java183
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StatsUtils.java110
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java56
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java351
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SuggestionResults.java89
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java67
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TextRange.java122
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java108
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UncachedInputMethodManagerUtils.java84
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java93
-rw-r--r--java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java106
-rw-r--r--java/src/com/android/inputmethod/latin/utils/XmlParseUtils.java83
303 files changed, 0 insertions, 63689 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java b/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java
deleted file mode 100644
index 37d910edb..000000000
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.R;
-
-// Handling long press timer to show a more keys keyboard.
-final class AccessibilityLongPressTimer extends Handler {
- public interface LongPressTimerCallback {
- public void performLongClickOn(Key key);
- }
-
- private static final int MSG_LONG_PRESS = 1;
-
- private final LongPressTimerCallback mCallback;
- private final long mConfigAccessibilityLongPressTimeout;
-
- public AccessibilityLongPressTimer(final LongPressTimerCallback callback,
- final Context context) {
- super();
- mCallback = callback;
- mConfigAccessibilityLongPressTimeout = context.getResources().getInteger(
- R.integer.config_accessibility_long_press_key_timeout);
- }
-
- @Override
- public void handleMessage(final Message msg) {
- switch (msg.what) {
- case MSG_LONG_PRESS:
- cancelLongPress();
- mCallback.performLongClickOn((Key)msg.obj);
- return;
- default:
- super.handleMessage(msg);
- return;
- }
- }
-
- public void startLongPress(final Key key) {
- cancelLongPress();
- final Message longPressMessage = obtainMessage(MSG_LONG_PRESS, key);
- sendMessageDelayed(longPressMessage, mConfigAccessibilityLongPressTimeout);
- }
-
- public void cancelLongPress() {
- removeMessages(MSG_LONG_PRESS);
- }
-}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
deleted file mode 100644
index 31e142e72..000000000
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.os.Build;
-import android.os.SystemClock;
-import android.provider.Settings;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.compat.SettingsSecureCompatUtils;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.utils.InputTypeUtils;
-
-public final class AccessibilityUtils {
- private static final String TAG = AccessibilityUtils.class.getSimpleName();
- private static final String CLASS = AccessibilityUtils.class.getName();
- private static final String PACKAGE =
- AccessibilityUtils.class.getPackage().getName();
-
- private static final AccessibilityUtils sInstance = new AccessibilityUtils();
-
- private Context mContext;
- private AccessibilityManager mAccessibilityManager;
- private AudioManager mAudioManager;
-
- /** The most recent auto-correction. */
- private String mAutoCorrectionWord;
-
- /** The most recent typed word for auto-correction. */
- private String mTypedWord;
-
- /*
- * Setting this constant to {@code false} will disable all keyboard
- * accessibility code, regardless of whether Accessibility is turned on in
- * the system settings. It should ONLY be used in the event of an emergency.
- */
- private static final boolean ENABLE_ACCESSIBILITY = true;
-
- public static void init(final Context context) {
- if (!ENABLE_ACCESSIBILITY) return;
-
- // These only need to be initialized if the kill switch is off.
- sInstance.initInternal(context);
- }
-
- public static AccessibilityUtils getInstance() {
- return sInstance;
- }
-
- private AccessibilityUtils() {
- // This class is not publicly instantiable.
- }
-
- private void initInternal(final Context context) {
- mContext = context;
- mAccessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- }
-
- /**
- * Returns {@code true} if accessibility is enabled. Currently, this means
- * that the kill switch is off and system accessibility is turned on.
- *
- * @return {@code true} if accessibility is enabled.
- */
- public boolean isAccessibilityEnabled() {
- return ENABLE_ACCESSIBILITY && mAccessibilityManager.isEnabled();
- }
-
- /**
- * Returns {@code true} if touch exploration is enabled. Currently, this
- * means that the kill switch is off, the device supports touch exploration,
- * and system accessibility is turned on.
- *
- * @return {@code true} if touch exploration is enabled.
- */
- public boolean isTouchExplorationEnabled() {
- return isAccessibilityEnabled() && mAccessibilityManager.isTouchExplorationEnabled();
- }
-
- /**
- * Returns {@true} if the provided event is a touch exploration (e.g. hover)
- * event. This is used to determine whether the event should be processed by
- * the touch exploration code within the keyboard.
- *
- * @param event The event to check.
- * @return {@true} is the event is a touch exploration event
- */
- public static boolean isTouchExplorationEvent(final MotionEvent event) {
- final int action = event.getAction();
- return action == MotionEvent.ACTION_HOVER_ENTER
- || action == MotionEvent.ACTION_HOVER_EXIT
- || action == MotionEvent.ACTION_HOVER_MOVE;
- }
-
- /**
- * Returns whether the device should obscure typed password characters.
- * Typically this means speaking "dot" in place of non-control characters.
- *
- * @return {@code true} if the device should obscure password characters.
- */
- @SuppressWarnings("deprecation")
- public boolean shouldObscureInput(final EditorInfo editorInfo) {
- if (editorInfo == null) return false;
-
- // The user can optionally force speaking passwords.
- if (SettingsSecureCompatUtils.ACCESSIBILITY_SPEAK_PASSWORD != null) {
- final boolean speakPassword = Settings.Secure.getInt(mContext.getContentResolver(),
- SettingsSecureCompatUtils.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0;
- if (speakPassword) return false;
- }
-
- // Always speak if the user is listening through headphones.
- if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) {
- return false;
- }
-
- // Don't speak if the IME is connected to a password field.
- return InputTypeUtils.isPasswordInputType(editorInfo.inputType);
- }
-
- /**
- * Sets the current auto-correction word and typed word. These may be used
- * to provide the user with a spoken description of what auto-correction
- * will occur when a key is typed.
- *
- * @param suggestedWords the list of suggested auto-correction words
- */
- public void setAutoCorrection(final SuggestedWords suggestedWords) {
- if (suggestedWords.mWillAutoCorrect) {
- mAutoCorrectionWord = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
- final SuggestedWords.SuggestedWordInfo typedWordInfo = suggestedWords.mTypedWordInfo;
- if (null == typedWordInfo) {
- mTypedWord = null;
- } else {
- mTypedWord = typedWordInfo.mWord;
- }
- } else {
- mAutoCorrectionWord = null;
- mTypedWord = null;
- }
- }
-
- /**
- * Obtains a description for an auto-correction key, taking into account the
- * currently typed word and auto-correction.
- *
- * @param keyCodeDescription spoken description of the key that will insert
- * an auto-correction
- * @param shouldObscure whether the key should be obscured
- * @return a description including a description of the auto-correction, if
- * needed
- */
- public String getAutoCorrectionDescription(
- final String keyCodeDescription, final boolean shouldObscure) {
- if (!TextUtils.isEmpty(mAutoCorrectionWord)) {
- if (!TextUtils.equals(mAutoCorrectionWord, mTypedWord)) {
- if (shouldObscure) {
- // This should never happen, but just in case...
- return mContext.getString(R.string.spoken_auto_correct_obscured,
- keyCodeDescription);
- }
- return mContext.getString(R.string.spoken_auto_correct, keyCodeDescription,
- mTypedWord, mAutoCorrectionWord);
- }
- }
-
- return keyCodeDescription;
- }
-
- /**
- * Sends the specified text to the {@link AccessibilityManager} to be
- * spoken.
- *
- * @param view The source view.
- * @param text The text to speak.
- */
- public void announceForAccessibility(final View view, final CharSequence text) {
- if (!mAccessibilityManager.isEnabled()) {
- Log.e(TAG, "Attempted to speak when accessibility was disabled!");
- return;
- }
-
- // The following is a hack to avoid using the heavy-weight TextToSpeech
- // class. Instead, we're just forcing a fake AccessibilityEvent into
- // the screen reader to make it speak.
- final AccessibilityEvent event = AccessibilityEvent.obtain();
-
- event.setPackageName(PACKAGE);
- event.setClassName(CLASS);
- event.setEventTime(SystemClock.uptimeMillis());
- event.setEnabled(true);
- event.getText().add(text);
-
- // Platforms starting at SDK version 16 (Build.VERSION_CODES.JELLY_BEAN) should use
- // announce events.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- event.setEventType(AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
- } else {
- event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- }
-
- final ViewParent viewParent = view.getParent();
- if ((viewParent == null) || !(viewParent instanceof ViewGroup)) {
- Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility");
- return;
- }
-
- viewParent.requestSendAccessibilityEvent(view, event);
- }
-
- /**
- * Handles speaking the "connect a headset to hear passwords" notification
- * when connecting to a password field.
- *
- * @param view The source view.
- * @param editorInfo The input connection's editor info attribute.
- * @param restarting Whether the connection is being restarted.
- */
- public void onStartInputViewInternal(final View view, final EditorInfo editorInfo,
- final boolean restarting) {
- if (shouldObscureInput(editorInfo)) {
- final CharSequence text = mContext.getText(R.string.spoken_use_headphones);
- announceForAccessibility(view, text);
- }
- }
-
- /**
- * Sends the specified {@link AccessibilityEvent} if accessibility is
- * enabled. No operation if accessibility is disabled.
- *
- * @param event The event to send.
- */
- public void requestSendAccessibilityEvent(final AccessibilityEvent event) {
- if (mAccessibilityManager.isEnabled()) {
- mAccessibilityManager.sendAccessibilityEvent(event);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
deleted file mode 100644
index bbda9f8e2..000000000
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.Locale;
-
-final class KeyCodeDescriptionMapper {
- private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName();
- private static final String SPOKEN_LETTER_RESOURCE_NAME_FORMAT = "spoken_accented_letter_%04X";
- private static final String SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT = "spoken_symbol_%04X";
- private static final String SPOKEN_EMOJI_RESOURCE_NAME_FORMAT = "spoken_emoji_%04X";
- private static final String SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX = "spoken_emoticon";
- private static final String SPOKEN_EMOTICON_CODE_POINT_FORMAT = "_%02X";
-
- // The resource ID of the string spoken for obscured keys
- private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
-
- private static final KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
-
- public static KeyCodeDescriptionMapper getInstance() {
- return sInstance;
- }
-
- // Sparse array of spoken description resource IDs indexed by key codes
- private final SparseIntArray mKeyCodeMap = new SparseIntArray();
-
- private KeyCodeDescriptionMapper() {
- // Special non-character codes defined in Keyboard
- mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space);
- mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete);
- mKeyCodeMap.put(Constants.CODE_ENTER, R.string.spoken_description_return);
- mKeyCodeMap.put(Constants.CODE_SETTINGS, R.string.spoken_description_settings);
- mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift);
- mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic);
- mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
- mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab);
- mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH,
- R.string.spoken_description_language_switch);
- mKeyCodeMap.put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next);
- mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS,
- R.string.spoken_description_action_previous);
- mKeyCodeMap.put(Constants.CODE_EMOJI, R.string.spoken_description_emoji);
- // Because the upper-case and lower-case mappings of the following letters is depending on
- // the locale, the upper case descriptions should be defined here. The lower case
- // descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
- // U+0049: "I" LATIN CAPITAL LETTER I
- // U+0069: "i" LATIN SMALL LETTER I
- // U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- mKeyCodeMap.put(0x0049, R.string.spoken_letter_0049);
- mKeyCodeMap.put(0x0130, R.string.spoken_letter_0130);
- }
-
- /**
- * Returns the localized description of the action performed by a specified
- * key based on the current keyboard state.
- *
- * @param context The package's context.
- * @param keyboard The keyboard on which the key resides.
- * @param key The key from which to obtain a description.
- * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
- * @return a character sequence describing the action performed by pressing the key
- */
- public String getDescriptionForKey(final Context context, final Keyboard keyboard,
- final Key key, final boolean shouldObscure) {
- final int code = key.getCode();
-
- if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
- final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
- if (description != null) {
- return description;
- }
- }
-
- if (code == Constants.CODE_SHIFT) {
- return getDescriptionForShiftKey(context, keyboard);
- }
-
- if (code == Constants.CODE_ENTER) {
- // The following function returns the correct description in all action and
- // regular enter cases, taking care of all modes.
- return getDescriptionForActionKey(context, keyboard, key);
- }
-
- if (code == Constants.CODE_OUTPUT_TEXT) {
- final String outputText = key.getOutputText();
- final String description = getSpokenEmoticonDescription(context, outputText);
- return TextUtils.isEmpty(description) ? outputText : description;
- }
-
- // Just attempt to speak the description.
- if (code != Constants.CODE_UNSPECIFIED) {
- // If the key description should be obscured, now is the time to do it.
- final boolean isDefinedNonCtrl = Character.isDefined(code)
- && !Character.isISOControl(code);
- if (shouldObscure && isDefinedNonCtrl) {
- return context.getString(OBSCURED_KEY_RES_ID);
- }
- final String description = getDescriptionForCodePoint(context, code);
- if (description != null) {
- return description;
- }
- if (!TextUtils.isEmpty(key.getLabel())) {
- return key.getLabel();
- }
- return context.getString(R.string.spoken_description_unknown);
- }
- return null;
- }
-
- /**
- * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL
- * key or {@code null} if there is not a description provided for the
- * current keyboard context.
- *
- * @param context The package's context.
- * @param keyboard The keyboard on which the key resides.
- * @return a character sequence describing the action performed by pressing the key
- */
- private static String getDescriptionForSwitchAlphaSymbol(final Context context,
- final Keyboard keyboard) {
- final KeyboardId keyboardId = keyboard.mId;
- final int elementId = keyboardId.mElementId;
- final int resId;
-
- switch (elementId) {
- case KeyboardId.ELEMENT_ALPHABET:
- case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
- resId = R.string.spoken_description_to_symbol;
- break;
- case KeyboardId.ELEMENT_SYMBOLS:
- case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
- resId = R.string.spoken_description_to_alpha;
- break;
- case KeyboardId.ELEMENT_PHONE:
- resId = R.string.spoken_description_to_symbol;
- break;
- case KeyboardId.ELEMENT_PHONE_SYMBOLS:
- resId = R.string.spoken_description_to_numeric;
- break;
- default:
- Log.e(TAG, "Missing description for keyboard element ID:" + elementId);
- return null;
- }
- return context.getString(resId);
- }
-
- /**
- * Returns a context-sensitive description of the "Shift" key.
- *
- * @param context The package's context.
- * @param keyboard The keyboard on which the key resides.
- * @return A context-sensitive description of the "Shift" key.
- */
- private static String getDescriptionForShiftKey(final Context context,
- final Keyboard keyboard) {
- final KeyboardId keyboardId = keyboard.mId;
- final int elementId = keyboardId.mElementId;
- final int resId;
-
- switch (elementId) {
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
- resId = R.string.spoken_description_caps_lock;
- break;
- case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
- resId = R.string.spoken_description_shift_shifted;
- break;
- case KeyboardId.ELEMENT_SYMBOLS:
- resId = R.string.spoken_description_symbols_shift;
- break;
- case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
- resId = R.string.spoken_description_symbols_shift_shifted;
- break;
- default:
- resId = R.string.spoken_description_shift;
- }
- return context.getString(resId);
- }
-
- /**
- * Returns a context-sensitive description of the "Enter" action key.
- *
- * @param context The package's context.
- * @param keyboard The keyboard on which the key resides.
- * @param key The key to describe.
- * @return Returns a context-sensitive description of the "Enter" action key.
- */
- private static String getDescriptionForActionKey(final Context context, final Keyboard keyboard,
- final Key key) {
- final KeyboardId keyboardId = keyboard.mId;
- final int actionId = keyboardId.imeAction();
- final int resId;
-
- // Always use the label, if available.
- if (!TextUtils.isEmpty(key.getLabel())) {
- return key.getLabel().trim();
- }
-
- // Otherwise, use the action ID.
- switch (actionId) {
- case EditorInfo.IME_ACTION_SEARCH:
- resId = R.string.spoken_description_search;
- break;
- case EditorInfo.IME_ACTION_GO:
- resId = R.string.label_go_key;
- break;
- case EditorInfo.IME_ACTION_SEND:
- resId = R.string.label_send_key;
- break;
- case EditorInfo.IME_ACTION_NEXT:
- resId = R.string.label_next_key;
- break;
- case EditorInfo.IME_ACTION_DONE:
- resId = R.string.label_done_key;
- break;
- case EditorInfo.IME_ACTION_PREVIOUS:
- resId = R.string.label_previous_key;
- break;
- default:
- resId = R.string.spoken_description_return;
- }
- return context.getString(resId);
- }
-
- /**
- * Returns a localized character sequence describing what will happen when
- * the specified key is pressed based on its key code point.
- *
- * @param context The package's context.
- * @param codePoint The code point from which to obtain a description.
- * @return a character sequence describing the code point.
- */
- public String getDescriptionForCodePoint(final Context context, final int codePoint) {
- // If the key description should be obscured, now is the time to do it.
- final int index = mKeyCodeMap.indexOfKey(codePoint);
- if (index >= 0) {
- return context.getString(mKeyCodeMap.valueAt(index));
- }
- final String accentedLetter = getSpokenAccentedLetterDescription(context, codePoint);
- if (accentedLetter != null) {
- return accentedLetter;
- }
- // Here, <code>code</code> may be a base (non-accented) letter.
- final String unsupportedSymbol = getSpokenSymbolDescription(context, codePoint);
- if (unsupportedSymbol != null) {
- return unsupportedSymbol;
- }
- final String emojiDescription = getSpokenEmojiDescription(context, codePoint);
- if (emojiDescription != null) {
- return emojiDescription;
- }
- if (Character.isDefined(codePoint) && !Character.isISOControl(codePoint)) {
- return StringUtils.newSingleCodePointString(codePoint);
- }
- return null;
- }
-
- // TODO: Remove this method once TTS supports those accented letters' verbalization.
- private String getSpokenAccentedLetterDescription(final Context context, final int code) {
- final boolean isUpperCase = Character.isUpperCase(code);
- final int baseCode = isUpperCase ? Character.toLowerCase(code) : code;
- final int baseIndex = mKeyCodeMap.indexOfKey(baseCode);
- final int resId = (baseIndex >= 0) ? mKeyCodeMap.valueAt(baseIndex)
- : getSpokenDescriptionId(context, baseCode, SPOKEN_LETTER_RESOURCE_NAME_FORMAT);
- if (resId == 0) {
- return null;
- }
- final String spokenText = context.getString(resId);
- return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText)
- : spokenText;
- }
-
- // TODO: Remove this method once TTS supports those symbols' verbalization.
- private String getSpokenSymbolDescription(final Context context, final int code) {
- final int resId = getSpokenDescriptionId(context, code, SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT);
- if (resId == 0) {
- return null;
- }
- final String spokenText = context.getString(resId);
- if (!TextUtils.isEmpty(spokenText)) {
- return spokenText;
- }
- // If a translated description is empty, fall back to unknown symbol description.
- return context.getString(R.string.spoken_symbol_unknown);
- }
-
- // TODO: Remove this method once TTS supports emoji verbalization.
- private String getSpokenEmojiDescription(final Context context, final int code) {
- final int resId = getSpokenDescriptionId(context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT);
- if (resId == 0) {
- return null;
- }
- final String spokenText = context.getString(resId);
- if (!TextUtils.isEmpty(spokenText)) {
- return spokenText;
- }
- // If a translated description is empty, fall back to unknown emoji description.
- return context.getString(R.string.spoken_emoji_unknown);
- }
-
- private int getSpokenDescriptionId(final Context context, final int code,
- final String resourceNameFormat) {
- final String resourceName = String.format(Locale.ROOT, resourceNameFormat, code);
- final Resources resources = context.getResources();
- // Note that the resource package name may differ from the context package name.
- final String resourcePackageName = resources.getResourcePackageName(
- R.string.spoken_description_unknown);
- final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName);
- if (resId != 0) {
- mKeyCodeMap.append(code, resId);
- }
- return resId;
- }
-
- // TODO: Remove this method once TTS supports emoticon verbalization.
- private static String getSpokenEmoticonDescription(final Context context,
- final String outputText) {
- final StringBuilder sb = new StringBuilder(SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX);
- final int textLength = outputText.length();
- for (int index = 0; index < textLength; index = outputText.offsetByCodePoints(index, 1)) {
- final int codePoint = outputText.codePointAt(index);
- sb.append(String.format(Locale.ROOT, SPOKEN_EMOTICON_CODE_POINT_FORMAT, codePoint));
- }
- final String resourceName = sb.toString();
- final Resources resources = context.getResources();
- // Note that the resource package name may differ from the context package name.
- final String resourcePackageName = resources.getResourcePackageName(
- R.string.spoken_description_unknown);
- final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName);
- return (resId == 0) ? null : resources.getString(resId);
- }
-}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
deleted file mode 100644
index 5c03d26a9..000000000
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.content.Context;
-import android.os.SystemClock;
-import androidx.core.view.AccessibilityDelegateCompat;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardView;
-
-/**
- * This class represents a delegate that can be registered in a class that extends
- * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance.
- *
- * To implement accessibility mode, the target keyboard view has to:<p>
- * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view.
- * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}.
- *
- * @param <KV> The keyboard view class type.
- */
-public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
- extends AccessibilityDelegateCompat {
- private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName();
- protected static final boolean DEBUG_HOVER = false;
-
- protected final KV mKeyboardView;
- protected final KeyDetector mKeyDetector;
- private Keyboard mKeyboard;
- private KeyboardAccessibilityNodeProvider<KV> mAccessibilityNodeProvider;
- private Key mLastHoverKey;
-
- public static final int HOVER_EVENT_POINTER_ID = 0;
-
- public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) {
- super();
- mKeyboardView = keyboardView;
- mKeyDetector = keyDetector;
-
- // Ensure that the view has an accessibility delegate.
- ViewCompat.setAccessibilityDelegate(keyboardView, this);
- }
-
- /**
- * Called when the keyboard layout changes.
- * <p>
- * <b>Note:</b> This method will be called even if accessibility is not
- * enabled.
- * @param keyboard The keyboard that is being set to the wrapping view.
- */
- public void setKeyboard(final Keyboard keyboard) {
- if (keyboard == null) {
- return;
- }
- if (mAccessibilityNodeProvider != null) {
- mAccessibilityNodeProvider.setKeyboard(keyboard);
- }
- mKeyboard = keyboard;
- }
-
- protected final Keyboard getKeyboard() {
- return mKeyboard;
- }
-
- protected final void setLastHoverKey(final Key key) {
- mLastHoverKey = key;
- }
-
- protected final Key getLastHoverKey() {
- return mLastHoverKey;
- }
-
- /**
- * Sends a window state change event with the specified string resource id.
- *
- * @param resId The string resource id of the text to send with the event.
- */
- protected void sendWindowStateChanged(final int resId) {
- if (resId == 0) {
- return;
- }
- final Context context = mKeyboardView.getContext();
- sendWindowStateChanged(context.getString(resId));
- }
-
- /**
- * Sends a window state change event with the specified text.
- *
- * @param text The text to send with the event.
- */
- protected void sendWindowStateChanged(final String text) {
- final AccessibilityEvent stateChange = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- mKeyboardView.onInitializeAccessibilityEvent(stateChange);
- stateChange.getText().add(text);
- stateChange.setContentDescription(null);
-
- final ViewParent parent = mKeyboardView.getParent();
- if (parent != null) {
- parent.requestSendAccessibilityEvent(mKeyboardView, stateChange);
- }
- }
-
- /**
- * Delegate method for View.getAccessibilityNodeProvider(). This method is called in SDK
- * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual
- * node hierarchy provider.
- *
- * @param host The host view for the provider.
- * @return The accessibility node provider for the current keyboard.
- */
- @Override
- public KeyboardAccessibilityNodeProvider<KV> getAccessibilityNodeProvider(final View host) {
- return getAccessibilityNodeProvider();
- }
-
- /**
- * @return A lazily-instantiated node provider for this view delegate.
- */
- protected KeyboardAccessibilityNodeProvider<KV> getAccessibilityNodeProvider() {
- // Instantiate the provide only when requested. Since the system
- // will call this method multiple times it is a good practice to
- // cache the provider instance.
- if (mAccessibilityNodeProvider == null) {
- mAccessibilityNodeProvider =
- new KeyboardAccessibilityNodeProvider<>(mKeyboardView, this);
- }
- return mAccessibilityNodeProvider;
- }
-
- /**
- * Get a key that a hover event is on.
- *
- * @param event The hover event.
- * @return key The key that the <code>event</code> is on.
- */
- protected final Key getHoverKeyOf(final MotionEvent event) {
- final int actionIndex = event.getActionIndex();
- final int x = (int)event.getX(actionIndex);
- final int y = (int)event.getY(actionIndex);
- return mKeyDetector.detectHitKey(x, y);
- }
-
- /**
- * Receives hover events when touch exploration is turned on in SDK versions ICS and higher.
- *
- * @param event The hover event.
- * @return {@code true} if the event is handled.
- */
- public boolean onHoverEvent(final MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- onHoverEnter(event);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- onHoverMove(event);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- onHoverExit(event);
- break;
- default:
- Log.w(getClass().getSimpleName(), "Unknown hover event: " + event);
- break;
- }
- return true;
- }
-
- /**
- * Process {@link MotionEvent#ACTION_HOVER_ENTER} event.
- *
- * @param event A hover enter event.
- */
- protected void onHoverEnter(final MotionEvent event) {
- final Key key = getHoverKeyOf(event);
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverEnter: key=" + key);
- }
- if (key != null) {
- onHoverEnterTo(key);
- }
- setLastHoverKey(key);
- }
-
- /**
- * Process {@link MotionEvent#ACTION_HOVER_MOVE} event.
- *
- * @param event A hover move event.
- */
- protected void onHoverMove(final MotionEvent event) {
- final Key lastKey = getLastHoverKey();
- final Key key = getHoverKeyOf(event);
- if (key != lastKey) {
- if (lastKey != null) {
- onHoverExitFrom(lastKey);
- }
- if (key != null) {
- onHoverEnterTo(key);
- }
- }
- if (key != null) {
- onHoverMoveWithin(key);
- }
- setLastHoverKey(key);
- }
-
- /**
- * Process {@link MotionEvent#ACTION_HOVER_EXIT} event.
- *
- * @param event A hover exit event.
- */
- protected void onHoverExit(final MotionEvent event) {
- final Key lastKey = getLastHoverKey();
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey);
- }
- if (lastKey != null) {
- onHoverExitFrom(lastKey);
- }
- final Key key = getHoverKeyOf(event);
- // Make sure we're not getting an EXIT event because the user slid
- // off the keyboard area, then force a key press.
- if (key != null) {
- onHoverExitFrom(key);
- }
- setLastHoverKey(null);
- }
-
- /**
- * Perform click on a key.
- *
- * @param key A key to be registered.
- */
- public void performClickOn(final Key key) {
- if (DEBUG_HOVER) {
- Log.d(TAG, "performClickOn: key=" + key);
- }
- simulateTouchEvent(MotionEvent.ACTION_DOWN, key);
- simulateTouchEvent(MotionEvent.ACTION_UP, key);
- }
-
- /**
- * Simulating a touch event by injecting a synthesized touch event into {@link KeyboardView}.
- *
- * @param touchAction The action of the synthesizing touch event.
- * @param key The key that a synthesized touch event is on.
- */
- private void simulateTouchEvent(final int touchAction, final Key key) {
- final int x = key.getHitBox().centerX();
- final int y = key.getHitBox().centerY();
- final long eventTime = SystemClock.uptimeMillis();
- final MotionEvent touchEvent = MotionEvent.obtain(
- eventTime, eventTime, touchAction, x, y, 0 /* metaState */);
- mKeyboardView.onTouchEvent(touchEvent);
- touchEvent.recycle();
- }
-
- /**
- * Handles a hover enter event on a key.
- *
- * @param key The currently hovered key.
- */
- protected void onHoverEnterTo(final Key key) {
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverEnterTo: key=" + key);
- }
- key.onPressed();
- mKeyboardView.invalidateKey(key);
- final KeyboardAccessibilityNodeProvider<KV> provider = getAccessibilityNodeProvider();
- provider.onHoverEnterTo(key);
- provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
- }
-
- /**
- * Handles a hover move event on a key.
- *
- * @param key The currently hovered key.
- */
- protected void onHoverMoveWithin(final Key key) { }
-
- /**
- * Handles a hover exit event on a key.
- *
- * @param key The currently hovered key.
- */
- protected void onHoverExitFrom(final Key key) {
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverExitFrom: key=" + key);
- }
- key.onReleased();
- mKeyboardView.invalidateKey(key);
- final KeyboardAccessibilityNodeProvider<KV> provider = getAccessibilityNodeProvider();
- provider.onHoverExitFrom(key);
- }
-
- /**
- * Perform long click on a key.
- *
- * @param key A key to be long pressed on.
- */
- public void performLongClickOn(final Key key) {
- // A extended class should override this method to implement long press.
- }
-}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
deleted file mode 100644
index cc244c305..000000000
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
-import androidx.core.view.accessibility.AccessibilityRecordCompat;
-import android.util.Log;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-import java.util.List;
-
-/**
- * Exposes a virtual view sub-tree for {@link KeyboardView} and generates
- * {@link AccessibilityEvent}s for individual {@link Key}s.
- * <p>
- * A virtual sub-tree is composed of imaginary {@link View}s that are reported
- * as a part of the view hierarchy for accessibility purposes. This enables
- * custom views that draw complex content to report them selves as a tree of
- * virtual views, thus conveying their logical structure.
- * </p>
- */
-final class KeyboardAccessibilityNodeProvider<KV extends KeyboardView>
- extends AccessibilityNodeProviderCompat {
- private static final String TAG = KeyboardAccessibilityNodeProvider.class.getSimpleName();
-
- // From {@link android.view.accessibility.AccessibilityNodeInfo#UNDEFINED_ITEM_ID}.
- private static final int UNDEFINED = Integer.MAX_VALUE;
-
- private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper;
- private final AccessibilityUtils mAccessibilityUtils;
-
- /** Temporary rect used to calculate in-screen bounds. */
- private final Rect mTempBoundsInScreen = new Rect();
-
- /** The parent view's cached on-screen location. */
- private final int[] mParentLocation = CoordinateUtils.newInstance();
-
- /** The virtual view identifier for the focused node. */
- private int mAccessibilityFocusedView = UNDEFINED;
-
- /** The virtual view identifier for the hovering node. */
- private int mHoveringNodeId = UNDEFINED;
-
- /** The keyboard view to provide an accessibility node info. */
- private final KV mKeyboardView;
- /** The accessibility delegate. */
- private final KeyboardAccessibilityDelegate<KV> mDelegate;
-
- /** The current keyboard. */
- private Keyboard mKeyboard;
-
- public KeyboardAccessibilityNodeProvider(final KV keyboardView,
- final KeyboardAccessibilityDelegate<KV> delegate) {
- super();
- mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance();
- mAccessibilityUtils = AccessibilityUtils.getInstance();
- mKeyboardView = keyboardView;
- mDelegate = delegate;
-
- // Since this class is constructed lazily, we might not get a subsequent
- // call to setKeyboard() and therefore need to call it now.
- setKeyboard(keyboardView.getKeyboard());
- }
-
- /**
- * Sets the keyboard represented by this node provider.
- *
- * @param keyboard The keyboard that is being set to the keyboard view.
- */
- public void setKeyboard(final Keyboard keyboard) {
- mKeyboard = keyboard;
- }
-
- private Key getKeyOf(final int virtualViewId) {
- if (mKeyboard == null) {
- return null;
- }
- final List<Key> sortedKeys = mKeyboard.getSortedKeys();
- // Use a virtual view id as an index of the sorted keys list.
- if (virtualViewId >= 0 && virtualViewId < sortedKeys.size()) {
- return sortedKeys.get(virtualViewId);
- }
- return null;
- }
-
- private int getVirtualViewIdOf(final Key key) {
- if (mKeyboard == null) {
- return View.NO_ID;
- }
- final List<Key> sortedKeys = mKeyboard.getSortedKeys();
- final int size = sortedKeys.size();
- for (int index = 0; index < size; index++) {
- if (sortedKeys.get(index) == key) {
- // Use an index of the sorted keys list as a virtual view id.
- return index;
- }
- }
- return View.NO_ID;
- }
-
- /**
- * Creates and populates an {@link AccessibilityEvent} for the specified key
- * and event type.
- *
- * @param key A key on the host keyboard view.
- * @param eventType The event type to create.
- * @return A populated {@link AccessibilityEvent} for the key.
- * @see AccessibilityEvent
- */
- public AccessibilityEvent createAccessibilityEvent(final Key key, final int eventType) {
- final int virtualViewId = getVirtualViewIdOf(key);
- final String keyDescription = getKeyDescription(key);
- final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- event.setPackageName(mKeyboardView.getContext().getPackageName());
- event.setClassName(key.getClass().getName());
- event.setContentDescription(keyDescription);
- event.setEnabled(true);
- final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
- record.setSource(mKeyboardView, virtualViewId);
- return event;
- }
-
- public void onHoverEnterTo(final Key key) {
- final int id = getVirtualViewIdOf(key);
- if (id == View.NO_ID) {
- return;
- }
- // Start hovering on the key. Because our accessibility model is lift-to-type, we should
- // report the node info without click and long click actions to avoid unnecessary
- // announcements.
- mHoveringNodeId = id;
- // Invalidate the node info of the key.
- sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
- sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
- }
-
- public void onHoverExitFrom(final Key key) {
- mHoveringNodeId = UNDEFINED;
- // Invalidate the node info of the key to be able to revert the change we have done
- // in {@link #onHoverEnterTo(Key)}.
- sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
- sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
- }
-
- /**
- * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual
- * view, i.e. a descendant of the host View, with the given <code>virtualViewId</code> or
- * the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
- * <p>
- * A virtual descendant is an imaginary View that is reported as a part of
- * the view hierarchy for accessibility purposes. This enables custom views
- * that draw complex content to report them selves as a tree of virtual
- * views, thus conveying their logical structure.
- * </p>
- * <p>
- * The implementer is responsible for obtaining an accessibility node info
- * from the pool of reusable instances and setting the desired properties of
- * the node info before returning it.
- * </p>
- *
- * @param virtualViewId A client defined virtual view id.
- * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant or the host
- * View.
- * @see AccessibilityNodeInfoCompat
- */
- @Override
- public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(final int virtualViewId) {
- if (virtualViewId == UNDEFINED) {
- return null;
- }
- if (virtualViewId == View.NO_ID) {
- // We are requested to create an AccessibilityNodeInfo describing
- // this View, i.e. the root of the virtual sub-tree.
- final AccessibilityNodeInfoCompat rootInfo =
- AccessibilityNodeInfoCompat.obtain(mKeyboardView);
- ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo);
- updateParentLocation();
-
- // Add the virtual children of the root View.
- final List<Key> sortedKeys = mKeyboard.getSortedKeys();
- final int size = sortedKeys.size();
- for (int index = 0; index < size; index++) {
- final Key key = sortedKeys.get(index);
- if (key.isSpacer()) {
- continue;
- }
- // Use an index of the sorted keys list as a virtual view id.
- rootInfo.addChild(mKeyboardView, index);
- }
- return rootInfo;
- }
-
- // Find the key that corresponds to the given virtual view id.
- final Key key = getKeyOf(virtualViewId);
- if (key == null) {
- Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
- return null;
- }
- final String keyDescription = getKeyDescription(key);
- final Rect boundsInParent = key.getHitBox();
-
- // Calculate the key's in-screen bounds.
- mTempBoundsInScreen.set(boundsInParent);
- mTempBoundsInScreen.offset(
- CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation));
- final Rect boundsInScreen = mTempBoundsInScreen;
-
- // Obtain and initialize an AccessibilityNodeInfo with information about the virtual view.
- final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
- info.setPackageName(mKeyboardView.getContext().getPackageName());
- // info.setTextEntryKey(true);
- info.setClassName(key.getClass().getName());
- info.setContentDescription(keyDescription);
- info.setBoundsInParent(boundsInParent);
- info.setBoundsInScreen(boundsInScreen);
- info.setParent(mKeyboardView);
- info.setSource(mKeyboardView, virtualViewId);
- info.setEnabled(key.isEnabled());
- info.setVisibleToUser(true);
- info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- if (key.isLongPressEnabled()) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK);
- }
-
- if (mAccessibilityFocusedView == virtualViewId) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
- } else {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
- }
- return info;
- }
-
- @Override
- public boolean performAction(final int virtualViewId, final int action,
- final Bundle arguments) {
- final Key key = getKeyOf(virtualViewId);
- if (key == null) {
- return false;
- }
- return performActionForKey(key, action);
- }
-
- /**
- * Performs the specified accessibility action for the given key.
- *
- * @param key The on which to perform the action.
- * @param action The action to perform.
- * @return The result of performing the action, or false if the action is not supported.
- */
- boolean performActionForKey(final Key key, final int action) {
- switch (action) {
- case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
- mAccessibilityFocusedView = getVirtualViewIdOf(key);
- sendAccessibilityEventForKey(
- key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
- return true;
- case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
- mAccessibilityFocusedView = UNDEFINED;
- sendAccessibilityEventForKey(
- key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
- return true;
- case AccessibilityNodeInfoCompat.ACTION_CLICK:
- sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_CLICKED);
- mDelegate.performClickOn(key);
- return true;
- case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK:
- sendAccessibilityEventForKey(key, AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- mDelegate.performLongClickOn(key);
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Sends an accessibility event for the given {@link Key}.
- *
- * @param key The key that's sending the event.
- * @param eventType The type of event to send.
- */
- void sendAccessibilityEventForKey(final Key key, final int eventType) {
- final AccessibilityEvent event = createAccessibilityEvent(key, eventType);
- mAccessibilityUtils.requestSendAccessibilityEvent(event);
- }
-
- /**
- * Returns the context-specific description for a {@link Key}.
- *
- * @param key The key to describe.
- * @return The context-specific description of the key.
- */
- private String getKeyDescription(final Key key) {
- final EditorInfo editorInfo = mKeyboard.mId.mEditorInfo;
- final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo);
- final SettingsValues currentSettings = Settings.getInstance().getCurrent();
- final String keyCodeDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
- mKeyboardView.getContext(), mKeyboard, key, shouldObscure);
- if (currentSettings.isWordSeparator(key.getCode())) {
- return mAccessibilityUtils.getAutoCorrectionDescription(
- keyCodeDescription, shouldObscure);
- }
- return keyCodeDescription;
- }
-
- /**
- * Updates the parent's on-screen location.
- */
- private void updateParentLocation() {
- mKeyboardView.getLocationOnScreen(mParentLocation);
- }
-}
diff --git a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
deleted file mode 100644
index 3234993cf..000000000
--- a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.MotionEvent;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-/**
- * This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance
- * accessibility support via composition rather via inheritance.
- */
-public final class MainKeyboardAccessibilityDelegate
- extends KeyboardAccessibilityDelegate<MainKeyboardView>
- implements AccessibilityLongPressTimer.LongPressTimerCallback {
- private static final String TAG = MainKeyboardAccessibilityDelegate.class.getSimpleName();
-
- /** Map of keyboard modes to resource IDs. */
- private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray();
-
- static {
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time);
- KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url);
- }
-
- /** The most recently set keyboard mode. */
- private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
- private static final int KEYBOARD_IS_HIDDEN = -1;
- // The rectangle region to ignore hover events.
- private final Rect mBoundsToIgnoreHoverEvent = new Rect();
-
-
- public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView,
- final KeyDetector keyDetector) {
- super(mainKeyboardView, keyDetector);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setKeyboard(final Keyboard keyboard) {
- if (keyboard == null) {
- return;
- }
- final Keyboard lastKeyboard = getKeyboard();
- super.setKeyboard(keyboard);
- final int lastKeyboardMode = mLastKeyboardMode;
- mLastKeyboardMode = keyboard.mId.mMode;
-
- // Since this method is called even when accessibility is off, make sure
- // to check the state before announcing anything.
- if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- return;
- }
- // Announce the language name only when the language is changed.
- if (lastKeyboard == null || !keyboard.mId.mSubtype.equals(lastKeyboard.mId.mSubtype)) {
- announceKeyboardLanguage(keyboard);
- return;
- }
- // Announce the mode only when the mode is changed.
- if (keyboard.mId.mMode != lastKeyboardMode) {
- announceKeyboardMode(keyboard);
- return;
- }
- // Announce the keyboard type only when the type is changed.
- if (keyboard.mId.mElementId != lastKeyboard.mId.mElementId) {
- announceKeyboardType(keyboard, lastKeyboard);
- return;
- }
- }
-
- /**
- * Called when the keyboard is hidden and accessibility is enabled.
- */
- public void onHideWindow() {
- if (mLastKeyboardMode != KEYBOARD_IS_HIDDEN) {
- announceKeyboardHidden();
- }
- mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
- }
-
- /**
- * Announces which language of keyboard is being displayed.
- *
- * @param keyboard The new keyboard.
- */
- private void announceKeyboardLanguage(final Keyboard keyboard) {
- final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(
- keyboard.mId.mSubtype.getRawSubtype());
- sendWindowStateChanged(languageText);
- }
-
- /**
- * Announces which type of keyboard is being displayed.
- * If the keyboard type is unknown, no announcement is made.
- *
- * @param keyboard The new keyboard.
- */
- private void announceKeyboardMode(final Keyboard keyboard) {
- final Context context = mKeyboardView.getContext();
- final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(keyboard.mId.mMode);
- if (modeTextResId == 0) {
- return;
- }
- final String modeText = context.getString(modeTextResId);
- final String text = context.getString(R.string.announce_keyboard_mode, modeText);
- sendWindowStateChanged(text);
- }
-
- /**
- * Announces which type of keyboard is being displayed.
- *
- * @param keyboard The new keyboard.
- * @param lastKeyboard The last keyboard.
- */
- private void announceKeyboardType(final Keyboard keyboard, final Keyboard lastKeyboard) {
- final int lastElementId = lastKeyboard.mId.mElementId;
- final int resId;
- switch (keyboard.mId.mElementId) {
- case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET:
- if (lastElementId == KeyboardId.ELEMENT_ALPHABET
- || lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
- // Transition between alphabet mode and automatic shifted mode should be silently
- // ignored because it can be determined by each key's talk back announce.
- return;
- }
- resId = R.string.spoken_description_mode_alpha;
- break;
- case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
- if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
- // Resetting automatic shifted mode by pressing the shift key causes the transition
- // from automatic shifted to manual shifted that should be silently ignored.
- return;
- }
- resId = R.string.spoken_description_shiftmode_on;
- break;
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
- if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
- // Resetting caps locked mode by pressing the shift key causes the transition
- // from shift locked to shift lock shifted that should be silently ignored.
- return;
- }
- resId = R.string.spoken_description_shiftmode_locked;
- break;
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
- resId = R.string.spoken_description_shiftmode_locked;
- break;
- case KeyboardId.ELEMENT_SYMBOLS:
- resId = R.string.spoken_description_mode_symbol;
- break;
- case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
- resId = R.string.spoken_description_mode_symbol_shift;
- break;
- case KeyboardId.ELEMENT_PHONE:
- resId = R.string.spoken_description_mode_phone;
- break;
- case KeyboardId.ELEMENT_PHONE_SYMBOLS:
- resId = R.string.spoken_description_mode_phone_shift;
- break;
- default:
- return;
- }
- sendWindowStateChanged(resId);
- }
-
- /**
- * Announces that the keyboard has been hidden.
- */
- private void announceKeyboardHidden() {
- sendWindowStateChanged(R.string.announce_keyboard_hidden);
- }
-
- @Override
- public void performClickOn(final Key key) {
- final int x = key.getHitBox().centerX();
- final int y = key.getHitBox().centerY();
- if (DEBUG_HOVER) {
- Log.d(TAG, "performClickOn: key=" + key
- + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
- }
- if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
- // This hover exit event points to the key that should be ignored.
- // Clear the ignoring region to handle further hover events.
- mBoundsToIgnoreHoverEvent.setEmpty();
- return;
- }
- super.performClickOn(key);
- }
-
- @Override
- protected void onHoverEnterTo(final Key key) {
- final int x = key.getHitBox().centerX();
- final int y = key.getHitBox().centerY();
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverEnterTo: key=" + key
- + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
- }
- if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
- return;
- }
- // This hover enter event points to the key that isn't in the ignoring region.
- // Further hover events should be handled.
- mBoundsToIgnoreHoverEvent.setEmpty();
- super.onHoverEnterTo(key);
- }
-
- @Override
- protected void onHoverExitFrom(final Key key) {
- final int x = key.getHitBox().centerX();
- final int y = key.getHitBox().centerY();
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverExitFrom: key=" + key
- + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
- }
- super.onHoverExitFrom(key);
- }
-
- @Override
- public void performLongClickOn(final Key key) {
- if (DEBUG_HOVER) {
- Log.d(TAG, "performLongClickOn: key=" + key);
- }
- final PointerTracker tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID);
- final long eventTime = SystemClock.uptimeMillis();
- final int x = key.getHitBox().centerX();
- final int y = key.getHitBox().centerY();
- final MotionEvent downEvent = MotionEvent.obtain(
- eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */);
- // Inject a fake down event to {@link PointerTracker} to handle a long press correctly.
- tracker.processMotionEvent(downEvent, mKeyDetector);
- downEvent.recycle();
- // Invoke {@link PointerTracker#onLongPressed()} as if a long press timeout has passed.
- tracker.onLongPressed();
- // If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout)
- // or a key invokes IME switcher dialog, we should just ignore the next
- // {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether
- // {@link PointerTracker} is in operation or not.
- if (tracker.isInOperation()) {
- // This long press shows a more keys keyboard and further hover events should be
- // handled.
- mBoundsToIgnoreHoverEvent.setEmpty();
- return;
- }
- // This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}.
- // We should ignore further hover events on this key.
- mBoundsToIgnoreHoverEvent.set(key.getHitBox());
- if (key.hasNoPanelAutoMoreKey()) {
- // This long press has registered a code point without showing a more keys keyboard.
- // We should talk back the code point if possible.
- final int codePointOfNoPanelAutoMoreKey = key.getMoreKeys()[0].mCode;
- final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint(
- mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey);
- if (text != null) {
- sendWindowStateChanged(text);
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java
deleted file mode 100644
index 4022da343..000000000
--- a/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.accessibility;
-
-import android.graphics.Rect;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
-import com.android.inputmethod.keyboard.PointerTracker;
-
-/**
- * This class represents a delegate that can be registered in {@link MoreKeysKeyboardView} to
- * enhance accessibility support via composition rather via inheritance.
- */
-public class MoreKeysKeyboardAccessibilityDelegate
- extends KeyboardAccessibilityDelegate<MoreKeysKeyboardView> {
- private static final String TAG = MoreKeysKeyboardAccessibilityDelegate.class.getSimpleName();
-
- private final Rect mMoreKeysKeyboardValidBounds = new Rect();
- private static final int CLOSING_INSET_IN_PIXEL = 1;
- private int mOpenAnnounceResId;
- private int mCloseAnnounceResId;
-
- public MoreKeysKeyboardAccessibilityDelegate(final MoreKeysKeyboardView moreKeysKeyboardView,
- final KeyDetector keyDetector) {
- super(moreKeysKeyboardView, keyDetector);
- }
-
- public void setOpenAnnounce(final int resId) {
- mOpenAnnounceResId = resId;
- }
-
- public void setCloseAnnounce(final int resId) {
- mCloseAnnounceResId = resId;
- }
-
- public void onShowMoreKeysKeyboard() {
- sendWindowStateChanged(mOpenAnnounceResId);
- }
-
- public void onDismissMoreKeysKeyboard() {
- sendWindowStateChanged(mCloseAnnounceResId);
- }
-
- @Override
- protected void onHoverEnter(final MotionEvent event) {
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverEnter: key=" + getHoverKeyOf(event));
- }
- super.onHoverEnter(event);
- final int actionIndex = event.getActionIndex();
- final int x = (int)event.getX(actionIndex);
- final int y = (int)event.getY(actionIndex);
- final int pointerId = event.getPointerId(actionIndex);
- final long eventTime = event.getEventTime();
- mKeyboardView.onDownEvent(x, y, pointerId, eventTime);
- }
-
- @Override
- protected void onHoverMove(final MotionEvent event) {
- super.onHoverMove(event);
- final int actionIndex = event.getActionIndex();
- final int x = (int)event.getX(actionIndex);
- final int y = (int)event.getY(actionIndex);
- final int pointerId = event.getPointerId(actionIndex);
- final long eventTime = event.getEventTime();
- mKeyboardView.onMoveEvent(x, y, pointerId, eventTime);
- }
-
- @Override
- protected void onHoverExit(final MotionEvent event) {
- final Key lastKey = getLastHoverKey();
- if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey);
- }
- if (lastKey != null) {
- super.onHoverExitFrom(lastKey);
- }
- setLastHoverKey(null);
- final int actionIndex = event.getActionIndex();
- final int x = (int)event.getX(actionIndex);
- final int y = (int)event.getY(actionIndex);
- final int pointerId = event.getPointerId(actionIndex);
- final long eventTime = event.getEventTime();
- // A hover exit event at one pixel width or height area on the edges of more keys keyboard
- // are treated as closing.
- mMoreKeysKeyboardValidBounds.set(0, 0, mKeyboardView.getWidth(), mKeyboardView.getHeight());
- mMoreKeysKeyboardValidBounds.inset(CLOSING_INSET_IN_PIXEL, CLOSING_INSET_IN_PIXEL);
- if (mMoreKeysKeyboardValidBounds.contains(x, y)) {
- // Invoke {@link MoreKeysKeyboardView#onUpEvent(int,int,int,long)} as if this hover
- // exit event selects a key.
- mKeyboardView.onUpEvent(x, y, pointerId, eventTime);
- // TODO: Should fix this reference. This is a hack to clear the state of
- // {@link PointerTracker}.
- PointerTracker.dismissAllMoreKeysPanels();
- return;
- }
- // Close the more keys keyboard.
- // TODO: Should fix this reference. This is a hack to clear the state of
- // {@link PointerTracker}.
- PointerTracker.dismissAllMoreKeysPanels();
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java b/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java
deleted file mode 100644
index 385e3e025..000000000
--- a/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.app.ActivityManager;
-import android.content.Context;
-
-import java.lang.reflect.Method;
-
-public class ActivityManagerCompatUtils {
- private static final Object LOCK = new Object();
- private static volatile Boolean sBoolean = null;
- private static final Method METHOD_isLowRamDevice = CompatUtils.getMethod(
- ActivityManager.class, "isLowRamDevice");
-
- private ActivityManagerCompatUtils() {
- // Do not instantiate this class.
- }
-
- public static boolean isLowRamDevice(Context context) {
- if (sBoolean == null) {
- synchronized(LOCK) {
- if (sBoolean == null) {
- final ActivityManager am =
- (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- sBoolean = (Boolean)CompatUtils.invoke(am, false, METHOD_isLowRamDevice);
- }
- }
- }
- return sBoolean;
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java b/java/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java
deleted file mode 100644
index f5e56eb4b..000000000
--- a/java/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.content.pm.PackageInfo;
-
-@SuppressWarnings("unused")
-public class AppWorkaroundsHelper {
- private AppWorkaroundsHelper() {
- // This helper class is not publicly instantiable.
- }
-
- public static boolean evaluateIsBrokenByRecorrection(final PackageInfo info) {
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java b/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
deleted file mode 100644
index 6e43cc9a7..000000000
--- a/java/src/com/android/inputmethod/compat/AppWorkaroundsUtils.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.content.pm.PackageInfo;
-import android.os.Build.VERSION_CODES;
-
-/**
- * A class to encapsulate work-arounds specific to particular apps.
- */
-public class AppWorkaroundsUtils {
- private final PackageInfo mPackageInfo; // May be null
- private final boolean mIsBrokenByRecorrection;
-
- public AppWorkaroundsUtils(final PackageInfo packageInfo) {
- mPackageInfo = packageInfo;
- mIsBrokenByRecorrection = AppWorkaroundsHelper.evaluateIsBrokenByRecorrection(
- packageInfo);
- }
-
- public boolean isBrokenByRecorrection() {
- return mIsBrokenByRecorrection;
- }
-
- public boolean isBeforeJellyBean() {
- if (null == mPackageInfo || null == mPackageInfo.applicationInfo) {
- return false;
- }
- return mPackageInfo.applicationInfo.targetSdkVersion < VERSION_CODES.JELLY_BEAN;
- }
-
- @Override
- public String toString() {
- if (null == mPackageInfo || null == mPackageInfo.applicationInfo) {
- return "";
- }
- final StringBuilder s = new StringBuilder();
- s.append("Target application : ")
- .append(mPackageInfo.applicationInfo.name)
- .append("\nPackage : ")
- .append(mPackageInfo.applicationInfo.packageName)
- .append("\nTarget app sdk version : ")
- .append(mPackageInfo.applicationInfo.targetSdkVersion);
- return s.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/BuildCompatUtils.java b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java
deleted file mode 100644
index 5d56f12ae..000000000
--- a/java/src/com/android/inputmethod/compat/BuildCompatUtils.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.os.Build;
-
-public final class BuildCompatUtils {
- private BuildCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- private static final boolean IS_RELEASE_BUILD = Build.VERSION.CODENAME.equals("REL");
-
- /**
- * The "effective" API version.
- * {@link android.os.Build.VERSION#SDK_INT} if the platform is a release build.
- * {@link android.os.Build.VERSION#SDK_INT} plus 1 if the platform is a development build.
- */
- public static final int EFFECTIVE_SDK_INT = IS_RELEASE_BUILD
- ? Build.VERSION.SDK_INT
- : Build.VERSION.SDK_INT + 1;
-}
diff --git a/java/src/com/android/inputmethod/compat/CharacterCompat.java b/java/src/com/android/inputmethod/compat/CharacterCompat.java
deleted file mode 100644
index 609fe1638..000000000
--- a/java/src/com/android/inputmethod/compat/CharacterCompat.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import java.lang.reflect.Method;
-
-public final class CharacterCompat {
- // Note that Character.isAlphabetic(int), has been introduced in API level 19
- // (Build.VERSION_CODE.KITKAT).
- private static final Method METHOD_isAlphabetic = CompatUtils.getMethod(
- Character.class, "isAlphabetic", int.class);
-
- private CharacterCompat() {
- // This utility class is not publicly instantiable.
- }
-
- public static boolean isAlphabetic(final int code) {
- if (METHOD_isAlphabetic != null) {
- return (Boolean)CompatUtils.invoke(null, false, METHOD_isAlphabetic, code);
- }
- switch (Character.getType(code)) {
- case Character.UPPERCASE_LETTER:
- case Character.LOWERCASE_LETTER:
- case Character.TITLECASE_LETTER:
- case Character.MODIFIER_LETTER:
- case Character.OTHER_LETTER:
- case Character.LETTER_NUMBER:
- return true;
- default:
- return false;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
deleted file mode 100644
index 5db80190c..000000000
--- a/java/src/com/android/inputmethod/compat/CompatUtils.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-public final class CompatUtils {
- private static final String TAG = CompatUtils.class.getSimpleName();
-
- private CompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static Class<?> getClass(final String className) {
- try {
- return Class.forName(className);
- } catch (final ClassNotFoundException e) {
- return null;
- }
- }
-
- public static Method getMethod(final Class<?> targetClass, final String name,
- final Class<?>... parameterTypes) {
- if (targetClass == null || TextUtils.isEmpty(name)) {
- return null;
- }
- try {
- return targetClass.getMethod(name, parameterTypes);
- } catch (final SecurityException | NoSuchMethodException e) {
- // ignore
- }
- return null;
- }
-
- public static Field getField(final Class<?> targetClass, final String name) {
- if (targetClass == null || TextUtils.isEmpty(name)) {
- return null;
- }
- try {
- return targetClass.getField(name);
- } catch (final SecurityException | NoSuchFieldException e) {
- // ignore
- }
- return null;
- }
-
- public static Constructor<?> getConstructor(final Class<?> targetClass,
- final Class<?> ... types) {
- if (targetClass == null || types == null) {
- return null;
- }
- try {
- return targetClass.getConstructor(types);
- } catch (final SecurityException | NoSuchMethodException e) {
- // ignore
- }
- return null;
- }
-
- public static Object newInstance(final Constructor<?> constructor, final Object ... args) {
- if (constructor == null) {
- return null;
- }
- try {
- return constructor.newInstance(args);
- } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException
- | InvocationTargetException e) {
- Log.e(TAG, "Exception in newInstance", e);
- }
- return null;
- }
-
- public static Object invoke(final Object receiver, final Object defaultValue,
- final Method method, final Object... args) {
- if (method == null) {
- return defaultValue;
- }
- try {
- return method.invoke(receiver, args);
- } catch (final IllegalAccessException | IllegalArgumentException
- | InvocationTargetException e) {
- Log.e(TAG, "Exception in invoke", e);
- }
- return defaultValue;
- }
-
- public static Object getFieldValue(final Object receiver, final Object defaultValue,
- final Field field) {
- if (field == null) {
- return defaultValue;
- }
- try {
- return field.get(receiver);
- } catch (final IllegalAccessException | IllegalArgumentException e) {
- Log.e(TAG, "Exception in getFieldValue", e);
- }
- return defaultValue;
- }
-
- public static void setFieldValue(final Object receiver, final Field field, final Object value) {
- if (field == null) {
- return;
- }
- try {
- field.set(receiver, value);
- } catch (final IllegalAccessException | IllegalArgumentException e) {
- Log.e(TAG, "Exception in setFieldValue", e);
- }
- }
-
- public static ClassWrapper getClassWrapper(final String className) {
- return new ClassWrapper(getClass(className));
- }
-
- public static final class ClassWrapper {
- private final Class<?> mClass;
- public ClassWrapper(final Class<?> targetClass) {
- mClass = targetClass;
- }
-
- public boolean exists() {
- return mClass != null;
- }
-
- public <T> ToObjectMethodWrapper<T> getMethod(final String name,
- final T defaultValue, final Class<?>... parameterTypes) {
- return new ToObjectMethodWrapper<>(CompatUtils.getMethod(mClass, name, parameterTypes),
- defaultValue);
- }
-
- public ToIntMethodWrapper getPrimitiveMethod(final String name, final int defaultValue,
- final Class<?>... parameterTypes) {
- return new ToIntMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes),
- defaultValue);
- }
-
- public ToFloatMethodWrapper getPrimitiveMethod(final String name, final float defaultValue,
- final Class<?>... parameterTypes) {
- return new ToFloatMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes),
- defaultValue);
- }
-
- public ToBooleanMethodWrapper getPrimitiveMethod(final String name,
- final boolean defaultValue, final Class<?>... parameterTypes) {
- return new ToBooleanMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes),
- defaultValue);
- }
- }
-
- public static final class ToObjectMethodWrapper<T> {
- private final Method mMethod;
- private final T mDefaultValue;
- public ToObjectMethodWrapper(final Method method, final T defaultValue) {
- mMethod = method;
- mDefaultValue = defaultValue;
- }
- @SuppressWarnings("unchecked")
- public T invoke(final Object receiver, final Object... args) {
- return (T) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
- }
- }
-
- public static final class ToIntMethodWrapper {
- private final Method mMethod;
- private final int mDefaultValue;
- public ToIntMethodWrapper(final Method method, final int defaultValue) {
- mMethod = method;
- mDefaultValue = defaultValue;
- }
- public int invoke(final Object receiver, final Object... args) {
- return (int) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
- }
- }
-
- public static final class ToFloatMethodWrapper {
- private final Method mMethod;
- private final float mDefaultValue;
- public ToFloatMethodWrapper(final Method method, final float defaultValue) {
- mMethod = method;
- mDefaultValue = defaultValue;
- }
- public float invoke(final Object receiver, final Object... args) {
- return (float) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
- }
- }
-
- public static final class ToBooleanMethodWrapper {
- private final Method mMethod;
- private final boolean mDefaultValue;
- public ToBooleanMethodWrapper(final Method method, final boolean defaultValue) {
- mMethod = method;
- mDefaultValue = defaultValue;
- }
- public boolean invoke(final Object receiver, final Object... args) {
- return (boolean) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/ConnectivityManagerCompatUtils.java b/java/src/com/android/inputmethod/compat/ConnectivityManagerCompatUtils.java
deleted file mode 100644
index b561f7a14..000000000
--- a/java/src/com/android/inputmethod/compat/ConnectivityManagerCompatUtils.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.net.ConnectivityManager;
-
-import java.lang.reflect.Method;
-
-public final class ConnectivityManagerCompatUtils {
- // ConnectivityManager#isActiveNetworkMetered() has been introduced
- // in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
- private static final Method METHOD_isActiveNetworkMetered = CompatUtils.getMethod(
- ConnectivityManager.class, "isActiveNetworkMetered");
-
- public static boolean isActiveNetworkMetered(final ConnectivityManager manager) {
- return (Boolean)CompatUtils.invoke(manager,
- // If the API telling whether the network is metered or not is not available,
- // then the closest thing is "if it's a mobile connection".
- manager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_MOBILE,
- METHOD_isActiveNetworkMetered);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
deleted file mode 100644
index 01a9e6712..000000000
--- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.annotation.TargetApi;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.os.Build;
-import android.view.inputmethod.CursorAnchorInfo;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * A wrapper for {@link CursorAnchorInfo}, which has been introduced in API Level 21. You can use
- * this wrapper to avoid direct dependency on newly introduced types.
- */
-public class CursorAnchorInfoCompatWrapper {
-
- /**
- * The insertion marker or character bounds have at least one visible region.
- */
- public static final int FLAG_HAS_VISIBLE_REGION = 0x01;
-
- /**
- * The insertion marker or character bounds have at least one invisible (clipped) region.
- */
- public static final int FLAG_HAS_INVISIBLE_REGION = 0x02;
-
- /**
- * The insertion marker or character bounds is placed at right-to-left (RTL) character.
- */
- public static final int FLAG_IS_RTL = 0x04;
-
- CursorAnchorInfoCompatWrapper() {
- // This class is not publicly instantiable.
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- @Nullable
- public static CursorAnchorInfoCompatWrapper wrap(@Nullable final CursorAnchorInfo instance) {
- if (BuildCompatUtils.EFFECTIVE_SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- return null;
- }
- if (instance == null) {
- return null;
- }
- return new RealWrapper(instance);
- }
-
- public int getSelectionStart() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public int getSelectionEnd() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public CharSequence getComposingText() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public int getComposingTextStart() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public Matrix getMatrix() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- @SuppressWarnings("unused")
- public RectF getCharacterBounds(final int index) {
- throw new UnsupportedOperationException("not supported.");
- }
-
- @SuppressWarnings("unused")
- public int getCharacterBoundsFlags(final int index) {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public float getInsertionMarkerBaseline() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public float getInsertionMarkerBottom() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public float getInsertionMarkerHorizontal() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public float getInsertionMarkerTop() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- public int getInsertionMarkerFlags() {
- throw new UnsupportedOperationException("not supported.");
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static final class RealWrapper extends CursorAnchorInfoCompatWrapper {
-
- @Nonnull
- private final CursorAnchorInfo mInstance;
-
- public RealWrapper(@Nonnull final CursorAnchorInfo info) {
- mInstance = info;
- }
-
- @Override
- public int getSelectionStart() {
- return mInstance.getSelectionStart();
- }
-
- @Override
- public int getSelectionEnd() {
- return mInstance.getSelectionEnd();
- }
-
- @Override
- public CharSequence getComposingText() {
- return mInstance.getComposingText();
- }
-
- @Override
- public int getComposingTextStart() {
- return mInstance.getComposingTextStart();
- }
-
- @Override
- public Matrix getMatrix() {
- return mInstance.getMatrix();
- }
-
- @Override
- public RectF getCharacterBounds(final int index) {
- return mInstance.getCharacterBounds(index);
- }
-
- @Override
- public int getCharacterBoundsFlags(final int index) {
- return mInstance.getCharacterBoundsFlags(index);
- }
-
- @Override
- public float getInsertionMarkerBaseline() {
- return mInstance.getInsertionMarkerBaseline();
- }
-
- @Override
- public float getInsertionMarkerBottom() {
- return mInstance.getInsertionMarkerBottom();
- }
-
- @Override
- public float getInsertionMarkerHorizontal() {
- return mInstance.getInsertionMarkerHorizontal();
- }
-
- @Override
- public float getInsertionMarkerTop() {
- return mInstance.getInsertionMarkerTop();
- }
-
- @Override
- public int getInsertionMarkerFlags() {
- return mInstance.getInsertionMarkerFlags();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
deleted file mode 100644
index 56ce8f5e6..000000000
--- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.view.inputmethod.EditorInfo;
-
-import java.lang.reflect.Field;
-import java.util.Locale;
-
-public final class EditorInfoCompatUtils {
- // Note that EditorInfo.IME_FLAG_FORCE_ASCII has been introduced
- // in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
- private static final Field FIELD_IME_FLAG_FORCE_ASCII = CompatUtils.getField(
- EditorInfo.class, "IME_FLAG_FORCE_ASCII");
- private static final Integer OBJ_IME_FLAG_FORCE_ASCII = (Integer) CompatUtils.getFieldValue(
- null /* receiver */, null /* defaultValue */, FIELD_IME_FLAG_FORCE_ASCII);
- private static final Field FIELD_HINT_LOCALES = CompatUtils.getField(
- EditorInfo.class, "hintLocales");
-
- private EditorInfoCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static boolean hasFlagForceAscii(final int imeOptions) {
- if (OBJ_IME_FLAG_FORCE_ASCII == null) return false;
- return (imeOptions & OBJ_IME_FLAG_FORCE_ASCII) != 0;
- }
-
- public static String imeActionName(final int imeOptions) {
- final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION;
- switch (actionId) {
- case EditorInfo.IME_ACTION_UNSPECIFIED:
- return "actionUnspecified";
- case EditorInfo.IME_ACTION_NONE:
- return "actionNone";
- case EditorInfo.IME_ACTION_GO:
- return "actionGo";
- case EditorInfo.IME_ACTION_SEARCH:
- return "actionSearch";
- case EditorInfo.IME_ACTION_SEND:
- return "actionSend";
- case EditorInfo.IME_ACTION_NEXT:
- return "actionNext";
- case EditorInfo.IME_ACTION_DONE:
- return "actionDone";
- case EditorInfo.IME_ACTION_PREVIOUS:
- return "actionPrevious";
- default:
- return "actionUnknown(" + actionId + ")";
- }
- }
-
- public static String imeOptionsName(final int imeOptions) {
- final String action = imeActionName(imeOptions);
- final StringBuilder flags = new StringBuilder();
- if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
- flags.append("flagNoEnterAction|");
- }
- if ((imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0) {
- flags.append("flagNavigateNext|");
- }
- if ((imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0) {
- flags.append("flagNavigatePrevious|");
- }
- if (hasFlagForceAscii(imeOptions)) {
- flags.append("flagForceAscii|");
- }
- return (action != null) ? flags + action : flags.toString();
- }
-
- public static Locale getPrimaryHintLocale(final EditorInfo editorInfo) {
- if (editorInfo == null) {
- return null;
- }
- final Object localeList = CompatUtils.getFieldValue(editorInfo, null, FIELD_HINT_LOCALES);
- if (localeList == null) {
- return null;
- }
- if (LocaleListCompatUtils.isEmpty(localeList)) {
- return null;
- }
- return LocaleListCompatUtils.get(localeList, 0);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
deleted file mode 100644
index a5c71b22f..000000000
--- a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-
-public final class InputConnectionCompatUtils {
- private static final CompatUtils.ClassWrapper sInputConnectionType;
- private static final CompatUtils.ToBooleanMethodWrapper sRequestCursorUpdatesMethod;
- static {
- sInputConnectionType = new CompatUtils.ClassWrapper(InputConnection.class);
- sRequestCursorUpdatesMethod = sInputConnectionType.getPrimitiveMethod(
- "requestCursorUpdates", false, int.class);
- }
-
- public static boolean isRequestCursorUpdatesAvailable() {
- return sRequestCursorUpdatesMethod != null;
- }
-
- /**
- * Local copies of some constants in InputConnection until the SDK becomes publicly available.
- */
- private static int CURSOR_UPDATE_IMMEDIATE = 1 << 0;
- private static int CURSOR_UPDATE_MONITOR = 1 << 1;
-
- private static boolean requestCursorUpdatesImpl(final InputConnection inputConnection,
- final int cursorUpdateMode) {
- if (!isRequestCursorUpdatesAvailable()) {
- return false;
- }
- return sRequestCursorUpdatesMethod.invoke(inputConnection, cursorUpdateMode);
- }
-
- /**
- * Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}.
- * @param inputConnection the input connection to which the request is to be sent.
- * @param enableMonitor {@code true} to request the editor to call back the method whenever the
- * cursor/anchor position is changed.
- * @param requestImmediateCallback {@code true} to request the editor to call back the method
- * as soon as possible to notify the current cursor/anchor position to the input method.
- * @return {@code false} if the request is not handled. Otherwise returns {@code true}.
- */
- public static boolean requestCursorUpdates(final InputConnection inputConnection,
- final boolean enableMonitor, final boolean requestImmediateCallback) {
- final int cursorUpdateMode = (enableMonitor ? CURSOR_UPDATE_MONITOR : 0)
- | (requestImmediateCallback ? CURSOR_UPDATE_IMMEDIATE : 0);
- return requestCursorUpdatesImpl(inputConnection, cursorUpdateMode);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
deleted file mode 100644
index aa20c0336..000000000
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.view.inputmethod.InputMethodManager;
-
-import java.lang.reflect.Method;
-
-public final class InputMethodManagerCompatWrapper {
- // Note that InputMethodManager.switchToNextInputMethod() has been introduced
- // in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
- private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod(
- InputMethodManager.class, "switchToNextInputMethod", IBinder.class, boolean.class);
-
- // Note that InputMethodManager.shouldOfferSwitchingToNextInputMethod() has been introduced
- // in API level 19 (Build.VERSION_CODES.KITKAT).
- private static final Method METHOD_shouldOfferSwitchingToNextInputMethod =
- CompatUtils.getMethod(InputMethodManager.class,
- "shouldOfferSwitchingToNextInputMethod", IBinder.class);
-
- public final InputMethodManager mImm;
-
- public InputMethodManagerCompatWrapper(final Context context) {
- mImm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
- }
-
- public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
- return (Boolean)CompatUtils.invoke(mImm, false /* defaultValue */,
- METHOD_switchToNextInputMethod, token, onlyCurrentIme);
- }
-
- public boolean shouldOfferSwitchingToNextInputMethod(final IBinder token) {
- return (Boolean)CompatUtils.invoke(mImm, false /* defaultValue */,
- METHOD_shouldOfferSwitchingToNextInputMethod, token);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
deleted file mode 100644
index 48047ddbf..000000000
--- a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.inputmethodservice.InputMethodService;
-
-import java.lang.reflect.Method;
-
-public final class InputMethodServiceCompatUtils {
- // Note that {@link InputMethodService#enableHardwareAcceleration} has been introduced
- // in API level 17 (Build.VERSION_CODES.JELLY_BEAN_MR1).
- private static final Method METHOD_enableHardwareAcceleration =
- CompatUtils.getMethod(InputMethodService.class, "enableHardwareAcceleration");
-
- private InputMethodServiceCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static boolean enableHardwareAcceleration(final InputMethodService ims) {
- return (Boolean)CompatUtils.invoke(ims, false /* defaultValue */,
- METHOD_enableHardwareAcceleration);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
deleted file mode 100644
index d123a1799..000000000
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.os.Build;
-import android.text.TextUtils;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-
-public final class InputMethodSubtypeCompatUtils {
- private static final String TAG = InputMethodSubtypeCompatUtils.class.getSimpleName();
- // Note that InputMethodSubtype(int nameId, int iconId, String locale, String mode,
- // String extraValue, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id)
- // has been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
- private static final Constructor<?> CONSTRUCTOR_INPUT_METHOD_SUBTYPE =
- CompatUtils.getConstructor(InputMethodSubtype.class,
- int.class, int.class, String.class, String.class, String.class, boolean.class,
- boolean.class, int.class);
- static {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- if (CONSTRUCTOR_INPUT_METHOD_SUBTYPE == null) {
- android.util.Log.w(TAG, "Warning!!! Constructor is not defined.");
- }
- }
- }
-
- // Note that {@link InputMethodSubtype#isAsciiCapable()} has been introduced in API level 19
- // (Build.VERSION_CODE.KITKAT).
- private static final Method METHOD_isAsciiCapable = CompatUtils.getMethod(
- InputMethodSubtype.class, "isAsciiCapable");
-
- private InputMethodSubtypeCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- @SuppressWarnings("deprecation")
- @Nonnull
- public static InputMethodSubtype newInputMethodSubtype(int nameId, int iconId, String locale,
- String mode, String extraValue, boolean isAuxiliary,
- boolean overridesImplicitlyEnabledSubtype, int id) {
- if (CONSTRUCTOR_INPUT_METHOD_SUBTYPE == null
- || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
- return new InputMethodSubtype(nameId, iconId, locale, mode, extraValue, isAuxiliary,
- overridesImplicitlyEnabledSubtype);
- }
- return (InputMethodSubtype) CompatUtils.newInstance(CONSTRUCTOR_INPUT_METHOD_SUBTYPE,
- nameId, iconId, locale, mode, extraValue, isAuxiliary,
- overridesImplicitlyEnabledSubtype, id);
- }
-
- public static boolean isAsciiCapable(final RichInputMethodSubtype subtype) {
- return isAsciiCapable(subtype.getRawSubtype());
- }
-
- public static boolean isAsciiCapable(final InputMethodSubtype subtype) {
- return isAsciiCapableWithAPI(subtype)
- || subtype.containsExtraValueKey(Constants.Subtype.ExtraValue.ASCII_CAPABLE);
- }
-
- // Note that InputMethodSubtype.getLanguageTag() is expected to be available in Android N+.
- private static final Method GET_LANGUAGE_TAG =
- CompatUtils.getMethod(InputMethodSubtype.class, "getLanguageTag");
-
- public static Locale getLocaleObject(final InputMethodSubtype subtype) {
- // Locale.forLanguageTag() is available only in Android L and later.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- final String languageTag = (String) CompatUtils.invoke(subtype, null, GET_LANGUAGE_TAG);
- if (!TextUtils.isEmpty(languageTag)) {
- return Locale.forLanguageTag(languageTag);
- }
- }
- return LocaleUtils.constructLocaleFromString(subtype.getLocale());
- }
-
- @UsedForTesting
- public static boolean isAsciiCapableWithAPI(final InputMethodSubtype subtype) {
- return (Boolean)CompatUtils.invoke(subtype, false, METHOD_isAsciiCapable);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/IntentCompatUtils.java b/java/src/com/android/inputmethod/compat/IntentCompatUtils.java
deleted file mode 100644
index 965a2a891..000000000
--- a/java/src/com/android/inputmethod/compat/IntentCompatUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.content.Intent;
-
-public final class IntentCompatUtils {
- // Note that Intent.ACTION_USER_INITIALIZE have been introduced in API level 17
- // (Build.VERSION_CODE.JELLY_BEAN_MR1).
- private static final String ACTION_USER_INITIALIZE =
- (String)CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
- CompatUtils.getField(Intent.class, "ACTION_USER_INITIALIZE"));
-
- private IntentCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static boolean is_ACTION_USER_INITIALIZE(final String action) {
- return ACTION_USER_INITIALIZE != null && ACTION_USER_INITIALIZE.equals(action);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/LocaleListCompatUtils.java b/java/src/com/android/inputmethod/compat/LocaleListCompatUtils.java
deleted file mode 100644
index 81ff0f994..000000000
--- a/java/src/com/android/inputmethod/compat/LocaleListCompatUtils.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import java.lang.reflect.Method;
-import java.util.Locale;
-
-public final class LocaleListCompatUtils {
- private static final Class CLASS_LocaleList = CompatUtils.getClass("android.os.LocaleList");
- private static final Method METHOD_get =
- CompatUtils.getMethod(CLASS_LocaleList, "get", int.class);
- private static final Method METHOD_isEmpty =
- CompatUtils.getMethod(CLASS_LocaleList, "isEmpty");
-
- private LocaleListCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static boolean isEmpty(final Object localeList) {
- return (Boolean) CompatUtils.invoke(localeList, Boolean.FALSE, METHOD_isEmpty);
- }
-
- public static Locale get(final Object localeList, final int index) {
- return (Locale) CompatUtils.invoke(localeList, null, METHOD_get, index);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java b/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java
deleted file mode 100644
index 58e5a36b6..000000000
--- a/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.text.Spannable;
-import android.text.Spanned;
-import android.text.style.LocaleSpan;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Locale;
-
-@UsedForTesting
-public final class LocaleSpanCompatUtils {
- private static final String TAG = LocaleSpanCompatUtils.class.getSimpleName();
-
- // Note that LocaleSpan(Locale locale) has been introduced in API level 17
- // (Build.VERSION_CODE.JELLY_BEAN_MR1).
- private static Class<?> getLocaleSpanClass() {
- try {
- return Class.forName("android.text.style.LocaleSpan");
- } catch (ClassNotFoundException e) {
- return null;
- }
- }
- private static final Class<?> LOCALE_SPAN_TYPE;
- private static final Constructor<?> LOCALE_SPAN_CONSTRUCTOR;
- private static final Method LOCALE_SPAN_GET_LOCALE;
- static {
- LOCALE_SPAN_TYPE = getLocaleSpanClass();
- LOCALE_SPAN_CONSTRUCTOR = CompatUtils.getConstructor(LOCALE_SPAN_TYPE, Locale.class);
- LOCALE_SPAN_GET_LOCALE = CompatUtils.getMethod(LOCALE_SPAN_TYPE, "getLocale");
- }
-
- @UsedForTesting
- public static boolean isLocaleSpanAvailable() {
- return (LOCALE_SPAN_CONSTRUCTOR != null && LOCALE_SPAN_GET_LOCALE != null);
- }
-
- @UsedForTesting
- public static Object newLocaleSpan(final Locale locale) {
- return CompatUtils.newInstance(LOCALE_SPAN_CONSTRUCTOR, locale);
- }
-
- @UsedForTesting
- public static Locale getLocaleFromLocaleSpan(final Object localeSpan) {
- return (Locale) CompatUtils.invoke(localeSpan, null, LOCALE_SPAN_GET_LOCALE);
- }
-
- /**
- * Ensures that the specified range is covered with only one {@link LocaleSpan} with the given
- * locale. If the region is already covered by one or more {@link LocaleSpan}, their ranges are
- * updated so that each character has only one locale.
- * @param spannable the spannable object to be updated.
- * @param start the start index from which {@link LocaleSpan} is attached (inclusive).
- * @param end the end index to which {@link LocaleSpan} is attached (exclusive).
- * @param locale the locale to be attached to the specified range.
- */
- @UsedForTesting
- public static void updateLocaleSpan(final Spannable spannable, final int start,
- final int end, final Locale locale) {
- if (end < start) {
- Log.e(TAG, "Invalid range: start=" + start + " end=" + end);
- return;
- }
- if (!isLocaleSpanAvailable()) {
- return;
- }
- // A brief summary of our strategy;
- // 1. Enumerate all LocaleSpans between [start - 1, end + 1].
- // 2. For each LocaleSpan S:
- // - Update the range of S so as not to cover [start, end] if S doesn't have the
- // expected locale.
- // - Mark S as "to be merged" if S has the expected locale.
- // 3. Merge all the LocaleSpans that are marked as "to be merged" into one LocaleSpan.
- // If no appropriate span is found, create a new one with newLocaleSpan method.
- final int searchStart = Math.max(start - 1, 0);
- final int searchEnd = Math.min(end + 1, spannable.length());
- // LocaleSpans found in the target range. See the step 1 in the above comment.
- final Object[] existingLocaleSpans = spannable.getSpans(searchStart, searchEnd,
- LOCALE_SPAN_TYPE);
- // LocaleSpans that are marked as "to be merged". See the step 2 in the above comment.
- final ArrayList<Object> existingLocaleSpansToBeMerged = new ArrayList<>();
- boolean isStartExclusive = true;
- boolean isEndExclusive = true;
- int newStart = start;
- int newEnd = end;
- for (final Object existingLocaleSpan : existingLocaleSpans) {
- final Locale attachedLocale = getLocaleFromLocaleSpan(existingLocaleSpan);
- if (!locale.equals(attachedLocale)) {
- // This LocaleSpan does not have the expected locale. Update its range if it has
- // an intersection with the range [start, end] (the first case of the step 2 in the
- // above comment).
- removeLocaleSpanFromRange(existingLocaleSpan, spannable, start, end);
- continue;
- }
- final int spanStart = spannable.getSpanStart(existingLocaleSpan);
- final int spanEnd = spannable.getSpanEnd(existingLocaleSpan);
- if (spanEnd < spanStart) {
- Log.e(TAG, "Invalid span: spanStart=" + spanStart + " spanEnd=" + spanEnd);
- continue;
- }
- if (spanEnd < start || end < spanStart) {
- // No intersection found.
- continue;
- }
-
- // Here existingLocaleSpan has the expected locale and an intersection with the
- // range [start, end] (the second case of the the step 2 in the above comment).
- final int spanFlag = spannable.getSpanFlags(existingLocaleSpan);
- if (spanStart < newStart) {
- newStart = spanStart;
- isStartExclusive = ((spanFlag & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) ==
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (newEnd < spanEnd) {
- newEnd = spanEnd;
- isEndExclusive = ((spanFlag & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) ==
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- existingLocaleSpansToBeMerged.add(existingLocaleSpan);
- }
-
- int originalLocaleSpanFlag = 0;
- Object localeSpan = null;
- if (existingLocaleSpansToBeMerged.isEmpty()) {
- // If there is no LocaleSpan that is marked as to be merged, create a new one.
- localeSpan = newLocaleSpan(locale);
- } else {
- // Reuse the first LocaleSpan to avoid unnecessary object instantiation.
- localeSpan = existingLocaleSpansToBeMerged.get(0);
- originalLocaleSpanFlag = spannable.getSpanFlags(localeSpan);
- // No need to keep other instances.
- for (int i = 1; i < existingLocaleSpansToBeMerged.size(); ++i) {
- spannable.removeSpan(existingLocaleSpansToBeMerged.get(i));
- }
- }
- final int localeSpanFlag = getSpanFlag(originalLocaleSpanFlag, isStartExclusive,
- isEndExclusive);
- spannable.setSpan(localeSpan, newStart, newEnd, localeSpanFlag);
- }
-
- private static void removeLocaleSpanFromRange(final Object localeSpan,
- final Spannable spannable, final int removeStart, final int removeEnd) {
- if (!isLocaleSpanAvailable()) {
- return;
- }
- final int spanStart = spannable.getSpanStart(localeSpan);
- final int spanEnd = spannable.getSpanEnd(localeSpan);
- if (spanStart > spanEnd) {
- Log.e(TAG, "Invalid span: spanStart=" + spanStart + " spanEnd=" + spanEnd);
- return;
- }
- if (spanEnd < removeStart) {
- // spanStart < spanEnd < removeStart < removeEnd
- return;
- }
- if (removeEnd < spanStart) {
- // spanStart < removeEnd < spanStart < spanEnd
- return;
- }
- final int spanFlags = spannable.getSpanFlags(localeSpan);
- if (spanStart < removeStart) {
- if (removeEnd < spanEnd) {
- // spanStart < removeStart < removeEnd < spanEnd
- final Locale locale = getLocaleFromLocaleSpan(localeSpan);
- spannable.setSpan(localeSpan, spanStart, removeStart, spanFlags);
- final Object attionalLocaleSpan = newLocaleSpan(locale);
- spannable.setSpan(attionalLocaleSpan, removeEnd, spanEnd, spanFlags);
- return;
- }
- // spanStart < removeStart < spanEnd <= removeEnd
- spannable.setSpan(localeSpan, spanStart, removeStart, spanFlags);
- return;
- }
- if (removeEnd < spanEnd) {
- // removeStart <= spanStart < removeEnd < spanEnd
- spannable.setSpan(localeSpan, removeEnd, spanEnd, spanFlags);
- return;
- }
- // removeStart <= spanStart < spanEnd < removeEnd
- spannable.removeSpan(localeSpan);
- }
-
- private static int getSpanFlag(final int originalFlag,
- final boolean isStartExclusive, final boolean isEndExclusive) {
- return (originalFlag & ~Spanned.SPAN_POINT_MARK_MASK) |
- getSpanPointMarkFlag(isStartExclusive, isEndExclusive);
- }
-
- private static int getSpanPointMarkFlag(final boolean isStartExclusive,
- final boolean isEndExclusive) {
- if (isStartExclusive) {
- return isEndExclusive ? Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
- : Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
- }
- return isEndExclusive ? Spanned.SPAN_INCLUSIVE_EXCLUSIVE
- : Spanned.SPAN_INCLUSIVE_INCLUSIVE;
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/LooperCompatUtils.java b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java
deleted file mode 100644
index d647dbbd3..000000000
--- a/java/src/com/android/inputmethod/compat/LooperCompatUtils.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.os.Looper;
-
-import java.lang.reflect.Method;
-
-/**
- * Helper to call Looper#quitSafely, which was introduced in API
- * level 18 (Build.VERSION_CODES.JELLY_BEAN_MR2).
- *
- * In unit tests, we create lots of instances of LatinIME, which means we need to clean up
- * some Loopers lest we leak file descriptors. In normal use on a device though, this is never
- * necessary (although it does not hurt).
- */
-public final class LooperCompatUtils {
- private static final Method METHOD_quitSafely = CompatUtils.getMethod(
- Looper.class, "quitSafely");
-
- public static void quitSafely(final Looper looper) {
- if (null != METHOD_quitSafely) {
- CompatUtils.invoke(looper, null /* default return value */, METHOD_quitSafely);
- } else {
- looper.quit();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java
deleted file mode 100644
index 70ab972c5..000000000
--- a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.app.Notification;
-import android.os.Build;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-public class NotificationCompatUtils {
- // Note that TextInfo.getCharSequence() is supposed to be available in API level 21 and later.
- private static final Method METHOD_setColor =
- CompatUtils.getMethod(Notification.Builder.class, "setColor", int.class);
- private static final Method METHOD_setVisibility =
- CompatUtils.getMethod(Notification.Builder.class, "setVisibility", int.class);
- private static final Method METHOD_setCategory =
- CompatUtils.getMethod(Notification.Builder.class, "setCategory", String.class);
- private static final Method METHOD_setPriority =
- CompatUtils.getMethod(Notification.Builder.class, "setPriority", int.class);
- private static final Method METHOD_build =
- CompatUtils.getMethod(Notification.Builder.class, "build");
- private static final Field FIELD_VISIBILITY_SECRET =
- CompatUtils.getField(Notification.class, "VISIBILITY_SECRET");
- private static final int VISIBILITY_SECRET = null == FIELD_VISIBILITY_SECRET ? 0
- : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
- FIELD_VISIBILITY_SECRET);
- private static final Field FIELD_CATEGORY_RECOMMENDATION =
- CompatUtils.getField(Notification.class, "CATEGORY_RECOMMENDATION");
- private static final String CATEGORY_RECOMMENDATION = null == FIELD_CATEGORY_RECOMMENDATION ? ""
- : (String) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
- FIELD_CATEGORY_RECOMMENDATION);
- private static final Field FIELD_PRIORITY_LOW =
- CompatUtils.getField(Notification.class, "PRIORITY_LOW");
- private static final int PRIORITY_LOW = null == FIELD_PRIORITY_LOW ? 0
- : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
- FIELD_PRIORITY_LOW);
-
- private NotificationCompatUtils() {
- // This class is non-instantiable.
- }
-
- // Sets the accent color
- public static void setColor(final Notification.Builder builder, final int color) {
- CompatUtils.invoke(builder, null, METHOD_setColor, color);
- }
-
- public static void setVisibilityToSecret(final Notification.Builder builder) {
- CompatUtils.invoke(builder, null, METHOD_setVisibility, VISIBILITY_SECRET);
- }
-
- public static void setCategoryToRecommendation(final Notification.Builder builder) {
- CompatUtils.invoke(builder, null, METHOD_setCategory, CATEGORY_RECOMMENDATION);
- }
-
- public static void setPriorityToLow(final Notification.Builder builder) {
- CompatUtils.invoke(builder, null, METHOD_setPriority, PRIORITY_LOW);
- }
-
- @SuppressWarnings("deprecation")
- public static Notification build(final Notification.Builder builder) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- // #build was added in API level 16, JELLY_BEAN
- return (Notification) CompatUtils.invoke(builder, null, METHOD_build);
- }
- // #getNotification was deprecated in API level 16, JELLY_BEAN
- return builder.getNotification();
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java b/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
deleted file mode 100644
index cfaf7ec77..000000000
--- a/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import java.lang.reflect.Field;
-
-public final class SettingsSecureCompatUtils {
- // Note that Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD has been introduced
- // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1).
- private static final Field FIELD_ACCESSIBILITY_SPEAK_PASSWORD = CompatUtils.getField(
- android.provider.Settings.Secure.class, "ACCESSIBILITY_SPEAK_PASSWORD");
-
- private SettingsSecureCompatUtils() {
- // This class is non-instantiable.
- }
-
- /**
- * Whether to speak passwords while in accessibility mode.
- */
- public static final String ACCESSIBILITY_SPEAK_PASSWORD = (String) CompatUtils.getFieldValue(
- null /* receiver */, null /* defaultValue */, FIELD_ACCESSIBILITY_SPEAK_PASSWORD);
-}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
deleted file mode 100644
index 3f621913c..000000000
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.content.Context;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.define.DebugFlags;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class SuggestionSpanUtils {
- // Note that SuggestionSpan.FLAG_AUTO_CORRECTION has been introduced
- // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1).
- private static final Field FIELD_FLAG_AUTO_CORRECTION = CompatUtils.getField(
- SuggestionSpan.class, "FLAG_AUTO_CORRECTION");
- private static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils.getFieldValue(
- null /* receiver */, null /* defaultValue */, FIELD_FLAG_AUTO_CORRECTION);
-
- static {
- if (DebugFlags.DEBUG_ENABLED) {
- if (OBJ_FLAG_AUTO_CORRECTION == null) {
- throw new RuntimeException("Field is accidentially null.");
- }
- }
- }
-
- private SuggestionSpanUtils() {
- // This utility class is not publicly instantiable.
- }
-
- @UsedForTesting
- public static CharSequence getTextWithAutoCorrectionIndicatorUnderline(
- final Context context, final String text, @Nonnull final Locale locale) {
- if (TextUtils.isEmpty(text) || OBJ_FLAG_AUTO_CORRECTION == null) {
- return text;
- }
- final Spannable spannable = new SpannableString(text);
- final SuggestionSpan suggestionSpan = new SuggestionSpan(context, locale,
- new String[] {} /* suggestions */, OBJ_FLAG_AUTO_CORRECTION, null);
- spannable.setSpan(suggestionSpan, 0, text.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
- return spannable;
- }
-
- @UsedForTesting
- public static CharSequence getTextWithSuggestionSpan(final Context context,
- final String pickedWord, final SuggestedWords suggestedWords, final Locale locale) {
- if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
- || suggestedWords.isPrediction() || suggestedWords.isPunctuationSuggestions()) {
- return pickedWord;
- }
-
- final ArrayList<String> suggestionsList = new ArrayList<>();
- for (int i = 0; i < suggestedWords.size(); ++i) {
- if (suggestionsList.size() >= SuggestionSpan.SUGGESTIONS_MAX_SIZE) {
- break;
- }
- final SuggestedWordInfo info = suggestedWords.getInfo(i);
- if (info.isKindOf(SuggestedWordInfo.KIND_PREDICTION)) {
- continue;
- }
- final String word = suggestedWords.getWord(i);
- if (!TextUtils.equals(pickedWord, word)) {
- suggestionsList.add(word.toString());
- }
- }
- final SuggestionSpan suggestionSpan = new SuggestionSpan(context, locale,
- suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */, null);
- final Spannable spannable = new SpannableString(pickedWord);
- spannable.setSpan(suggestionSpan, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- return spannable;
- }
-
- /**
- * Returns first {@link Locale} found in the given array of {@link SuggestionSpan}.
- * @param suggestionSpans the array of {@link SuggestionSpan} to be examined.
- * @return the first {@link Locale} found in {@code suggestionSpans}. {@code null} when not
- * found.
- */
- @UsedForTesting
- @Nullable
- public static Locale findFirstLocaleFromSuggestionSpans(
- final SuggestionSpan[] suggestionSpans) {
- for (final SuggestionSpan suggestionSpan : suggestionSpans) {
- final String localeString = suggestionSpan.getLocale();
- if (TextUtils.isEmpty(localeString)) {
- continue;
- }
- return LocaleUtils.constructLocaleFromString(localeString);
- }
- return null;
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
deleted file mode 100644
index d8d2be04c..000000000
--- a/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.view.textservice.SuggestionsInfo;
-
-import java.lang.reflect.Field;
-
-public final class SuggestionsInfoCompatUtils {
- // Note that SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS has been introduced
- // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1).
- private static final Field FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS =
- CompatUtils.getField(SuggestionsInfo.class, "RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS");
- private static final Integer OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS =
- (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
- FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS);
- private static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS =
- OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS != null
- ? OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS : 0;
-
- private SuggestionsInfoCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- /**
- * Returns the flag value of the attributes of the suggestions that can be obtained by
- * {@link SuggestionsInfo#getSuggestionsAttributes()}: this tells that the text service thinks
- * the result suggestions include highly recommended ones.
- */
- public static int getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS() {
- return RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS;
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/TextInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/TextInfoCompatUtils.java
deleted file mode 100644
index 09f39a756..000000000
--- a/java/src/com/android/inputmethod/compat/TextInfoCompatUtils.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.view.textservice.TextInfo;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-
-@UsedForTesting
-public final class TextInfoCompatUtils {
- // Note that TextInfo.getCharSequence() is supposed to be available in API level 21 and later.
- private static final Method TEXT_INFO_GET_CHAR_SEQUENCE =
- CompatUtils.getMethod(TextInfo.class, "getCharSequence");
- private static final Constructor<?> TEXT_INFO_CONSTRUCTOR_FOR_CHAR_SEQUENCE =
- CompatUtils.getConstructor(TextInfo.class, CharSequence.class, int.class, int.class,
- int.class, int.class);
-
- @UsedForTesting
- public static boolean isCharSequenceSupported() {
- return TEXT_INFO_GET_CHAR_SEQUENCE != null &&
- TEXT_INFO_CONSTRUCTOR_FOR_CHAR_SEQUENCE != null;
- }
-
- @UsedForTesting
- public static TextInfo newInstance(CharSequence charSequence, int start, int end, int cookie,
- int sequenceNumber) {
- if (TEXT_INFO_CONSTRUCTOR_FOR_CHAR_SEQUENCE != null) {
- return (TextInfo) CompatUtils.newInstance(TEXT_INFO_CONSTRUCTOR_FOR_CHAR_SEQUENCE,
- charSequence, start, end, cookie, sequenceNumber);
- }
- return new TextInfo(charSequence.subSequence(start, end).toString(), cookie,
- sequenceNumber);
- }
-
- /**
- * Returns the result of {@link TextInfo#getCharSequence()} when available. Otherwise returns
- * the result of {@link TextInfo#getText()} as fall back.
- * @param textInfo the instance for which {@link TextInfo#getCharSequence()} or
- * {@link TextInfo#getText()} is called.
- * @return the result of {@link TextInfo#getCharSequence()} when available. Otherwise returns
- * the result of {@link TextInfo#getText()} as fall back. If {@code textInfo} is {@code null},
- * returns {@code null}.
- */
- @UsedForTesting
- public static CharSequence getCharSequenceOrString(final TextInfo textInfo) {
- final CharSequence defaultValue = (textInfo == null ? null : textInfo.getText());
- return (CharSequence) CompatUtils.invoke(textInfo, defaultValue,
- TEXT_INFO_GET_CHAR_SEQUENCE);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java b/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java
deleted file mode 100644
index f8e1902c0..000000000
--- a/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.graphics.drawable.Drawable;
-import android.widget.TextView;
-
-import java.lang.reflect.Method;
-
-public final class TextViewCompatUtils {
- // Note that TextView.setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable,Drawable,
- // Drawable,Drawable) has been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
- private static final Method METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds =
- CompatUtils.getMethod(TextView.class, "setCompoundDrawablesRelativeWithIntrinsicBounds",
- Drawable.class, Drawable.class, Drawable.class, Drawable.class);
-
- private TextViewCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static void setCompoundDrawablesRelativeWithIntrinsicBounds(final TextView textView,
- final Drawable start, final Drawable top, final Drawable end, final Drawable bottom) {
- if (METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds == null) {
- textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
- return;
- }
- CompatUtils.invoke(textView, null, METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds,
- start, top, end, bottom);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
deleted file mode 100644
index 3633d667d..000000000
--- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.provider.UserDictionary;
-
-import java.util.Locale;
-
-public final class UserDictionaryCompatUtils {
- @SuppressWarnings("deprecation")
- public static void addWord(final Context context, final String word,
- final int freq, final String shortcut, final Locale locale) {
- if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- addWordWithShortcut(context, word, freq, shortcut, locale);
- return;
- }
- // Fall back to the pre-JellyBean method.
- final Locale currentLocale = context.getResources().getConfiguration().locale;
- final int localeType = currentLocale.equals(locale)
- ? UserDictionary.Words.LOCALE_TYPE_CURRENT : UserDictionary.Words.LOCALE_TYPE_ALL;
- UserDictionary.Words.addWord(context, word, freq, localeType);
- }
-
- // {@link UserDictionary.Words#addWord(Context,String,int,String,Locale)} was introduced
- // in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private static void addWordWithShortcut(final Context context, final String word,
- final int freq, final String shortcut, final Locale locale) {
- UserDictionary.Words.addWord(context, word, freq, shortcut, locale);
- }
-}
-
diff --git a/java/src/com/android/inputmethod/compat/UserManagerCompatUtils.java b/java/src/com/android/inputmethod/compat/UserManagerCompatUtils.java
deleted file mode 100644
index a0ca2c9f2..000000000
--- a/java/src/com/android/inputmethod/compat/UserManagerCompatUtils.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.UserManager;
-import androidx.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.reflect.Method;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-/**
- * A temporary solution until {@code UserManagerCompat.isUserUnlocked()} in the support-v4 library
- * becomes publicly available.
- */
-public final class UserManagerCompatUtils {
- private static final Method METHOD_isUserUnlocked;
-
- static {
- // We do not try to search the method in Android M and prior.
- if (BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.M) {
- METHOD_isUserUnlocked = null;
- } else {
- METHOD_isUserUnlocked = CompatUtils.getMethod(UserManager.class, "isUserUnlocked");
- }
- }
-
- private UserManagerCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static final int LOCK_STATE_UNKNOWN = 0;
- public static final int LOCK_STATE_UNLOCKED = 1;
- public static final int LOCK_STATE_LOCKED = 2;
-
- @Retention(SOURCE)
- @IntDef({LOCK_STATE_UNKNOWN, LOCK_STATE_UNLOCKED, LOCK_STATE_LOCKED})
- public @interface LockState {}
-
- /**
- * Check if the calling user is running in an "unlocked" state. A user is unlocked only after
- * they've entered their credentials (such as a lock pattern or PIN), and credential-encrypted
- * private app data storage is available.
- * @param context context from which {@link UserManager} should be obtained.
- * @return One of {@link LockState}.
- */
- @LockState
- public static int getUserLockState(final Context context) {
- if (METHOD_isUserUnlocked == null) {
- return LOCK_STATE_UNKNOWN;
- }
- final UserManager userManager = context.getSystemService(UserManager.class);
- if (userManager == null) {
- return LOCK_STATE_UNKNOWN;
- }
- final Boolean result =
- (Boolean) CompatUtils.invoke(userManager, null, METHOD_isUserUnlocked);
- if (result == null) {
- return LOCK_STATE_UNKNOWN;
- }
- return result ? LOCK_STATE_UNLOCKED : LOCK_STATE_LOCKED;
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
deleted file mode 100644
index a584625e2..000000000
--- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.view.View;
-
-import java.lang.reflect.Method;
-
-// TODO: Use {@link androidx.core.view.ViewCompat} instead of this utility class.
-// Currently {@link #getPaddingEnd(View)} and {@link #setPaddingRelative(View,int,int,int,int)}
-// are missing from android-support-v4 static library in KitKat SDK.
-public final class ViewCompatUtils {
- // Note that View.getPaddingEnd(), View.setPaddingRelative(int,int,int,int) have been
- // introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
- private static final Method METHOD_getPaddingEnd = CompatUtils.getMethod(
- View.class, "getPaddingEnd");
- private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod(
- View.class, "setPaddingRelative",
- int.class, int.class, int.class, int.class);
- // Note that View.setTextAlignment(int) has been introduced in API level 17.
- private static final Method METHOD_setTextAlignment = CompatUtils.getMethod(
- View.class, "setTextAlignment", int.class);
-
- private ViewCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static int getPaddingEnd(final View view) {
- if (METHOD_getPaddingEnd == null) {
- return view.getPaddingRight();
- }
- return (Integer)CompatUtils.invoke(view, 0, METHOD_getPaddingEnd);
- }
-
- public static void setPaddingRelative(final View view, final int start, final int top,
- final int end, final int bottom) {
- if (METHOD_setPaddingRelative == null) {
- view.setPadding(start, top, end, bottom);
- return;
- }
- CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom);
- }
-
- // These TEXT_ALIGNMENT_* constants have been introduced in API 17.
- public static final int TEXT_ALIGNMENT_INHERIT = 0;
- public static final int TEXT_ALIGNMENT_GRAVITY = 1;
- public static final int TEXT_ALIGNMENT_TEXT_START = 2;
- public static final int TEXT_ALIGNMENT_TEXT_END = 3;
- public static final int TEXT_ALIGNMENT_CENTER = 4;
- public static final int TEXT_ALIGNMENT_VIEW_START = 5;
- public static final int TEXT_ALIGNMENT_VIEW_END = 6;
-
- public static void setTextAlignment(final View view, final int textAlignment) {
- CompatUtils.invoke(view, null, METHOD_setTextAlignment, textAlignment);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java
deleted file mode 100644
index 0c8e5b77d..000000000
--- a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.view.View;
-
-public class ViewOutlineProviderCompatUtils {
- private ViewOutlineProviderCompatUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public interface InsetsUpdater {
- public void setInsets(final InputMethodService.Insets insets);
- }
-
- private static final InsetsUpdater EMPTY_INSETS_UPDATER = new InsetsUpdater() {
- @Override
- public void setInsets(final InputMethodService.Insets insets) {}
- };
-
- public static InsetsUpdater setInsetsOutlineProvider(final View view) {
- if (BuildCompatUtils.EFFECTIVE_SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- return EMPTY_INSETS_UPDATER;
- }
- return ViewOutlineProviderCompatUtilsLXX.setInsetsOutlineProvider(view);
- }
-}
diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java
deleted file mode 100644
index 5bbb5ce99..000000000
--- a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.compat;
-
-import android.annotation.TargetApi;
-import android.graphics.Outline;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater;
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-class ViewOutlineProviderCompatUtilsLXX {
- private ViewOutlineProviderCompatUtilsLXX() {
- // This utility class is not publicly instantiable.
- }
-
- static InsetsUpdater setInsetsOutlineProvider(final View view) {
- final InsetsOutlineProvider provider = new InsetsOutlineProvider(view);
- view.setOutlineProvider(provider);
- return provider;
- }
-
- private static class InsetsOutlineProvider extends ViewOutlineProvider
- implements InsetsUpdater {
- private final View mView;
- private static final int NO_DATA = -1;
- private int mLastVisibleTopInsets = NO_DATA;
-
- public InsetsOutlineProvider(final View view) {
- mView = view;
- view.setOutlineProvider(this);
- }
-
- @Override
- public void setInsets(final InputMethodService.Insets insets) {
- final int visibleTopInsets = insets.visibleTopInsets;
- if (mLastVisibleTopInsets != visibleTopInsets) {
- mLastVisibleTopInsets = visibleTopInsets;
- mView.invalidateOutline();
- }
- }
-
- @Override
- public void getOutline(final View view, final Outline outline) {
- if (mLastVisibleTopInsets == NO_DATA) {
- // Call default implementation.
- ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
- return;
- }
- // TODO: Revisit this when floating/resize keyboard is supported.
- outline.setRect(
- view.getLeft(), mLastVisibleTopInsets, view.getRight(), view.getBottom());
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
deleted file mode 100644
index 1b526d453..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.DownloadManager.Request;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.latin.BinaryDictionaryFileDumper;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.utils.ApplicationUtils;
-import com.android.inputmethod.latin.utils.DebugLogUtils;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Object representing an upgrade from one state to another.
- *
- * This implementation basically encapsulates a list of Runnable objects. In the future
- * it may manage dependencies between them. Concretely, it does not use Runnable because the
- * actions need an argument.
- */
-/*
-
-The state of a word list follows the following scheme.
-
- | ^
- MakeAvailable |
- | .------------Forget--------'
- V |
- STATUS_AVAILABLE <-------------------------.
- | |
-StartDownloadAction FinishDeleteAction
- | |
- V |
-STATUS_DOWNLOADING EnableAction-- STATUS_DELETING
- | | ^
-InstallAfterDownloadAction | |
- | .---------------' StartDeleteAction
- | | |
- V V |
- STATUS_INSTALLED <--EnableAction-- STATUS_DISABLED
- --DisableAction-->
-
- It may also be possible that DisableAction or StartDeleteAction or
- DownloadAction run when the file is still downloading. This cancels
- the download and returns to STATUS_AVAILABLE.
- Also, an UpdateDataAction may apply in any state. It does not affect
- the state in any way (nor type, local filename, id or version) but
- may update other attributes like description or remote filename.
-
- Forget is an DB maintenance action that removes the entry if it is not installed or disabled.
- This happens when the word list information disappeared from the server, or when a new version
- is available and we should forget about the old one.
-*/
-public final class ActionBatch {
- /**
- * A piece of update.
- *
- * Action is basically like a Runnable that takes an argument.
- */
- public interface Action {
- /**
- * Execute this action NOW.
- * @param context the context to get system services, resources, databases
- */
- void execute(final Context context);
- }
-
- /**
- * An action that starts downloading an available word list.
- */
- public static final class StartDownloadAction implements Action {
- static final String TAG = "DictionaryProvider:" + StartDownloadAction.class.getSimpleName();
-
- private final String mClientId;
- // The data to download. May not be null.
- final WordListMetadata mWordList;
- public StartDownloadAction(final String clientId, final WordListMetadata wordList) {
- DebugLogUtils.l("New download action for client ", clientId, " : ", wordList);
- mClientId = clientId;
- mWordList = wordList;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "UpdateAction with a null parameter!");
- return;
- }
- DebugLogUtils.l("Downloading word list");
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
- if (MetadataDbHelper.STATUS_DOWNLOADING == status) {
- // The word list is still downloading. Cancel the download and revert the
- // word list status to "available".
- manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
- MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
- } else if (MetadataDbHelper.STATUS_AVAILABLE != status
- && MetadataDbHelper.STATUS_RETRYING != status) {
- // Should never happen
- Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' : " + status
- + " for an upgrade action. Fall back to download.");
- }
- // Download it.
- DebugLogUtils.l("Upgrade word list, downloading", mWordList.mRemoteFilename);
-
- // This is an upgraded word list: we should download it.
- // Adding a disambiguator to circumvent a bug in older versions of DownloadManager.
- // DownloadManager also stupidly cuts the extension to replace with its own that it
- // gets from the content-type. We need to circumvent this.
- final String disambiguator = "#" + System.currentTimeMillis()
- + ApplicationUtils.getVersionName(context) + ".dict";
- final Uri uri = Uri.parse(mWordList.mRemoteFilename + disambiguator);
- final Request request = new Request(uri);
-
- final Resources res = context.getResources();
- request.setAllowedNetworkTypes(Request.NETWORK_WIFI | Request.NETWORK_MOBILE);
- request.setTitle(mWordList.mDescription);
- request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
- request.setVisibleInDownloadsUi(
- res.getBoolean(R.bool.dict_downloads_visible_in_download_UI));
-
- final long downloadId = UpdateHandler.registerDownloadRequest(manager, request, db,
- mWordList.mId, mWordList.mVersion);
- Log.i(TAG, String.format("Starting the dictionary download with version:"
- + " %d and Url: %s", mWordList.mVersion, uri));
- DebugLogUtils.l("Starting download of", uri, "with id", downloadId);
- PrivateLog.log("Starting download of " + uri + ", id : " + downloadId);
- }
- }
-
- /**
- * An action that updates the database to reflect the status of a newly installed word list.
- */
- public static final class InstallAfterDownloadAction implements Action {
- static final String TAG = "DictionaryProvider:"
- + InstallAfterDownloadAction.class.getSimpleName();
- private final String mClientId;
- // The state to upgrade from. May not be null.
- final ContentValues mWordListValues;
-
- public InstallAfterDownloadAction(final String clientId,
- final ContentValues wordListValues) {
- DebugLogUtils.l("New InstallAfterDownloadAction for client ", clientId, " : ",
- wordListValues);
- mClientId = clientId;
- mWordListValues = wordListValues;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordListValues) {
- Log.e(TAG, "InstallAfterDownloadAction with a null parameter!");
- return;
- }
- final int status = mWordListValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_DOWNLOADING != status) {
- final String id = mWordListValues.getAsString(MetadataDbHelper.WORDLISTID_COLUMN);
- Log.e(TAG, "Unexpected state of the word list '" + id + "' : " + status
- + " for an InstallAfterDownload action. Bailing out.");
- return;
- }
-
- DebugLogUtils.l("Setting word list as installed");
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- MetadataDbHelper.markEntryAsFinishedDownloadingAndInstalled(db, mWordListValues);
-
- // Install the downloaded file by un-compressing and moving it to the staging
- // directory. Ideally, we should do this before updating the DB, but the
- // installDictToStagingFromContentProvider() relies on the db being updated.
- final String localeString = mWordListValues.getAsString(MetadataDbHelper.LOCALE_COLUMN);
- BinaryDictionaryFileDumper.installDictToStagingFromContentProvider(
- LocaleUtils.constructLocaleFromString(localeString), context, false);
- }
- }
-
- /**
- * An action that enables an existing word list.
- */
- public static final class EnableAction implements Action {
- static final String TAG = "DictionaryProvider:" + EnableAction.class.getSimpleName();
- private final String mClientId;
- // The state to upgrade from. May not be null.
- final WordListMetadata mWordList;
-
- public EnableAction(final String clientId, final WordListMetadata wordList) {
- DebugLogUtils.l("New EnableAction for client ", clientId, " : ", wordList);
- mClientId = clientId;
- mWordList = wordList;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) {
- Log.e(TAG, "EnableAction with a null parameter!");
- return;
- }
- DebugLogUtils.l("Enabling word list");
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_DISABLED != status
- && MetadataDbHelper.STATUS_DELETING != status) {
- Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + " : " + status
- + " for an enable action. Cancelling");
- return;
- }
- MetadataDbHelper.markEntryAsEnabled(db, mWordList.mId, mWordList.mVersion);
- }
- }
-
- /**
- * An action that disables a word list.
- */
- public static final class DisableAction implements Action {
- static final String TAG = "DictionaryProvider:" + DisableAction.class.getSimpleName();
- private final String mClientId;
- // The word list to disable. May not be null.
- final WordListMetadata mWordList;
- public DisableAction(final String clientId, final WordListMetadata wordlist) {
- DebugLogUtils.l("New Disable action for client ", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "DisableAction with a null word list!");
- return;
- }
- DebugLogUtils.l("Disabling word list : " + mWordList);
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_INSTALLED == status) {
- // Disabling an installed word list
- MetadataDbHelper.markEntryAsDisabled(db, mWordList.mId, mWordList.mVersion);
- } else {
- if (MetadataDbHelper.STATUS_DOWNLOADING != status) {
- Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' : "
- + status + " for a disable action. Fall back to marking as available.");
- }
- // The word list is still downloading. Cancel the download and revert the
- // word list status to "available".
- final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
- manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
- MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
- }
- }
- }
-
- /**
- * An action that makes a word list available.
- */
- public static final class MakeAvailableAction implements Action {
- static final String TAG = "DictionaryProvider:" + MakeAvailableAction.class.getSimpleName();
- private final String mClientId;
- // The word list to make available. May not be null.
- final WordListMetadata mWordList;
- public MakeAvailableAction(final String clientId, final WordListMetadata wordlist) {
- DebugLogUtils.l("New MakeAvailable action", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "MakeAvailableAction with a null word list!");
- return;
- }
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- if (null != MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion)) {
- Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' "
- + " for a makeavailable action. Marking as available anyway.");
- }
- DebugLogUtils.l("Making word list available : " + mWordList);
- // If mLocalFilename is null, then it's a remote file that hasn't been downloaded
- // yet, so we set the local filename to the empty string.
- final ContentValues values = MetadataDbHelper.makeContentValues(0,
- MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_AVAILABLE,
- mWordList.mId, mWordList.mLocale, mWordList.mDescription,
- null == mWordList.mLocalFilename ? "" : mWordList.mLocalFilename,
- mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
- mWordList.mChecksum, mWordList.mRetryCount, mWordList.mFileSize,
- mWordList.mVersion, mWordList.mFormatVersion);
- PrivateLog.log("Insert 'available' record for " + mWordList.mDescription
- + " and locale " + mWordList.mLocale);
- db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
- }
- }
-
- /**
- * An action that marks a word list as pre-installed.
- *
- * This is almost the same as MakeAvailableAction, as it only inserts a line with parameters
- * received from outside.
- * Unlike MakeAvailableAction, the parameters are not received from a downloaded metadata file
- * but from the client directly; it marks a word list as being "installed" and not "available".
- * It also explicitly sets the filename to the empty string, so that we don't try to open
- * it on our side.
- */
- public static final class MarkPreInstalledAction implements Action {
- static final String TAG = "DictionaryProvider:"
- + MarkPreInstalledAction.class.getSimpleName();
- private final String mClientId;
- // The word list to mark pre-installed. May not be null.
- final WordListMetadata mWordList;
- public MarkPreInstalledAction(final String clientId, final WordListMetadata wordlist) {
- DebugLogUtils.l("New MarkPreInstalled action", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "MarkPreInstalledAction with a null word list!");
- return;
- }
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- if (null != MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion)) {
- Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' "
- + " for a markpreinstalled action. Marking as preinstalled anyway.");
- }
- DebugLogUtils.l("Marking word list preinstalled : " + mWordList);
- // This word list is pre-installed : we don't have its file. We should reset
- // the local file name to the empty string so that we don't try to open it
- // accidentally. The remote filename may be set by the application if it so wishes.
- final ContentValues values = MetadataDbHelper.makeContentValues(0,
- MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_INSTALLED,
- mWordList.mId, mWordList.mLocale, mWordList.mDescription,
- TextUtils.isEmpty(mWordList.mLocalFilename) ? "" : mWordList.mLocalFilename,
- mWordList.mRemoteFilename, mWordList.mLastUpdate,
- mWordList.mRawChecksum, mWordList.mChecksum, mWordList.mRetryCount,
- mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
- PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription
- + " and locale " + mWordList.mLocale);
- db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
- }
- }
-
- /**
- * An action that updates information about a word list - description, locale etc
- */
- public static final class UpdateDataAction implements Action {
- static final String TAG = "DictionaryProvider:" + UpdateDataAction.class.getSimpleName();
- private final String mClientId;
- final WordListMetadata mWordList;
- public UpdateDataAction(final String clientId, final WordListMetadata wordlist) {
- DebugLogUtils.l("New UpdateData action for client ", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "UpdateDataAction with a null word list!");
- return;
- }
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- ContentValues oldValues = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- if (null == oldValues) {
- Log.e(TAG, "Trying to update data about a non-existing word list. Bailing out.");
- return;
- }
- DebugLogUtils.l("Updating data about a word list : " + mWordList);
- final ContentValues values = MetadataDbHelper.makeContentValues(
- oldValues.getAsInteger(MetadataDbHelper.PENDINGID_COLUMN),
- oldValues.getAsInteger(MetadataDbHelper.TYPE_COLUMN),
- oldValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN),
- mWordList.mId, mWordList.mLocale, mWordList.mDescription,
- oldValues.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN),
- mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
- mWordList.mChecksum, mWordList.mRetryCount, mWordList.mFileSize,
- mWordList.mVersion, mWordList.mFormatVersion);
- PrivateLog.log("Updating record for " + mWordList.mDescription
- + " and locale " + mWordList.mLocale);
- db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
- MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
- + MetadataDbHelper.VERSION_COLUMN + " = ?",
- new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
- }
- }
-
- /**
- * An action that deletes the metadata about a word list if possible.
- *
- * This is triggered when a specific word list disappeared from the server, or when a fresher
- * word list is available and the old one was not installed.
- * If the word list has not been installed, it's possible to delete its associated metadata.
- * Otherwise, the settings are retained so that the user can still administrate it.
- */
- public static final class ForgetAction implements Action {
- static final String TAG = "DictionaryProvider:" + ForgetAction.class.getSimpleName();
- private final String mClientId;
- // The word list to remove. May not be null.
- final WordListMetadata mWordList;
- final boolean mHasNewerVersion;
- public ForgetAction(final String clientId, final WordListMetadata wordlist,
- final boolean hasNewerVersion) {
- DebugLogUtils.l("New TryRemove action for client ", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- mHasNewerVersion = hasNewerVersion;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "TryRemoveAction with a null word list!");
- return;
- }
- DebugLogUtils.l("Trying to remove word list : " + mWordList);
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- if (null == values) {
- Log.e(TAG, "Trying to update the metadata of a non-existing wordlist. Cancelling.");
- return;
- }
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (mHasNewerVersion && MetadataDbHelper.STATUS_AVAILABLE != status) {
- // If we have a newer version of this word list, we should be here ONLY if it was
- // not installed - else we should be upgrading it.
- Log.e(TAG, "Unexpected status for forgetting a word list info : " + status
- + ", removing URL to prevent re-download");
- }
- if (MetadataDbHelper.STATUS_INSTALLED == status
- || MetadataDbHelper.STATUS_DISABLED == status
- || MetadataDbHelper.STATUS_DELETING == status) {
- // If it is installed or disabled, we need to mark it as deleted so that LatinIME
- // will remove it next time it enquires for dictionaries.
- // If it is deleting and we don't have a new version, then we have to wait until
- // LatinIME actually has deleted it before we can remove its metadata.
- // In both cases, remove the URI from the database since it is not supposed to
- // be accessible any more.
- values.put(MetadataDbHelper.REMOTE_FILENAME_COLUMN, "");
- values.put(MetadataDbHelper.STATUS_COLUMN, MetadataDbHelper.STATUS_DELETING);
- db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
- MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
- + MetadataDbHelper.VERSION_COLUMN + " = ?",
- new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
- } else {
- // If it's AVAILABLE or DOWNLOADING or even UNKNOWN, delete the entry.
- db.delete(MetadataDbHelper.METADATA_TABLE_NAME,
- MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
- + MetadataDbHelper.VERSION_COLUMN + " = ?",
- new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
- }
- }
- }
-
- /**
- * An action that sets the word list for deletion as soon as possible.
- *
- * This is triggered when the user requests deletion of a word list. This will mark it as
- * deleted in the database, and fire an intent for Android Keyboard to take notice and
- * reload its dictionaries right away if it is up. If it is not up now, then it will
- * delete the actual file the next time it gets up.
- * A file marked as deleted causes the content provider to supply a zero-sized file to
- * Android Keyboard, which will overwrite any existing file and provide no words for this
- * word list. This is not exactly a "deletion", since there is an actual file which takes up
- * a few bytes on the disk, but this allows to override a default dictionary with an empty
- * dictionary. This way, there is no need for the user to make a distinction between
- * dictionaries installed by default and add-on dictionaries.
- */
- public static final class StartDeleteAction implements Action {
- static final String TAG = "DictionaryProvider:" + StartDeleteAction.class.getSimpleName();
- private final String mClientId;
- // The word list to delete. May not be null.
- final WordListMetadata mWordList;
- public StartDeleteAction(final String clientId, final WordListMetadata wordlist) {
- DebugLogUtils.l("New StartDelete action for client ", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "StartDeleteAction with a null word list!");
- return;
- }
- DebugLogUtils.l("Trying to delete word list : " + mWordList);
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- if (null == values) {
- Log.e(TAG, "Trying to set a non-existing wordlist for removal. Cancelling.");
- return;
- }
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_DISABLED != status) {
- Log.e(TAG, "Unexpected status for deleting a word list info : " + status);
- }
- MetadataDbHelper.markEntryAsDeleting(db, mWordList.mId, mWordList.mVersion);
- }
- }
-
- /**
- * An action that validates a word list as deleted.
- *
- * This will restore the word list as available if it still is, or remove the entry if
- * it is not any more.
- */
- public static final class FinishDeleteAction implements Action {
- static final String TAG = "DictionaryProvider:" + FinishDeleteAction.class.getSimpleName();
- private final String mClientId;
- // The word list to delete. May not be null.
- final WordListMetadata mWordList;
- public FinishDeleteAction(final String clientId, final WordListMetadata wordlist) {
- DebugLogUtils.l("New FinishDelete action for client", clientId, " : ", wordlist);
- mClientId = clientId;
- mWordList = wordlist;
- }
-
- @Override
- public void execute(final Context context) {
- if (null == mWordList) { // This should never happen
- Log.e(TAG, "FinishDeleteAction with a null word list!");
- return;
- }
- DebugLogUtils.l("Trying to delete word list : " + mWordList);
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- mWordList.mId, mWordList.mVersion);
- if (null == values) {
- Log.e(TAG, "Trying to set a non-existing wordlist for removal. Cancelling.");
- return;
- }
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_DELETING != status) {
- Log.e(TAG, "Unexpected status for finish-deleting a word list info : " + status);
- }
- final String remoteFilename =
- values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
- // If there isn't a remote filename any more, then we don't know where to get the file
- // from any more, so we remove the entry entirely. As a matter of fact, if the file was
- // marked DELETING but disappeared from the metadata on the server, it ended up
- // this way.
- if (TextUtils.isEmpty(remoteFilename)) {
- db.delete(MetadataDbHelper.METADATA_TABLE_NAME,
- MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
- + MetadataDbHelper.VERSION_COLUMN + " = ?",
- new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
- } else {
- MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
- }
- }
- }
-
- // An action batch consists of an ordered queue of Actions that can execute.
- private final Queue<Action> mActions;
-
- public ActionBatch() {
- mActions = new LinkedList<>();
- }
-
- public void add(final Action a) {
- mActions.add(a);
- }
-
- /**
- * Append all the actions of another action batch.
- * @param that the upgrade to merge into this one.
- */
- public void append(final ActionBatch that) {
- for (final Action a : that.mActions) {
- add(a);
- }
- }
-
- /**
- * Execute this batch.
- *
- * @param context the context for getting resources, databases, system services.
- * @param reporter a Reporter to send errors to.
- */
- public void execute(final Context context, final ProblemReporter reporter) {
- DebugLogUtils.l("Executing a batch of actions");
- Queue<Action> remainingActions = mActions;
- while (!remainingActions.isEmpty()) {
- final Action a = remainingActions.poll();
- try {
- a.execute(context);
- } catch (Exception e) {
- if (null != reporter)
- reporter.report(e);
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/AssetFileAddress.java b/java/src/com/android/inputmethod/dictionarypack/AssetFileAddress.java
deleted file mode 100644
index bebb59fc0..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/AssetFileAddress.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import java.io.File;
-
-/**
- * Immutable class to hold the address of an asset.
- * As opposed to a normal file, an asset is usually represented as a contiguous byte array in
- * the package file. Open it correctly thus requires the name of the package it is in, but
- * also the offset in the file and the length of this data. This class encapsulates these three.
- */
-final class AssetFileAddress {
- public final String mFilename;
- public final long mOffset;
- public final long mLength;
-
- public AssetFileAddress(final String filename, final long offset, final long length) {
- mFilename = filename;
- mOffset = offset;
- mLength = length;
- }
-
- /**
- * Makes an AssetFileAddress. This may return null.
- *
- * @param filename the filename.
- * @return the address, or null if the file does not exist or the parameters are not valid.
- */
- public static AssetFileAddress makeFromFileName(final String filename) {
- if (null == filename) return null;
- final File f = new File(filename);
- if (!f.isFile()) return null;
- return new AssetFileAddress(filename, 0l, f.length());
- }
-
- /**
- * Makes an AssetFileAddress. This may return null.
- *
- * @param filename the filename.
- * @param offset the offset.
- * @param length the length.
- * @return the address, or null if the file does not exist or the parameters are not valid.
- */
- public static AssetFileAddress makeFromFileNameAndOffset(final String filename,
- final long offset, final long length) {
- if (null == filename) return null;
- final File f = new File(filename);
- if (!f.isFile()) return null;
- return new AssetFileAddress(filename, offset, length);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/BadFormatException.java b/java/src/com/android/inputmethod/dictionarypack/BadFormatException.java
deleted file mode 100644
index d3090ddb0..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/BadFormatException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-/**
- * Exception thrown when the metadata for the dictionary does not comply to a known format.
- */
-public final class BadFormatException extends Exception {
- public BadFormatException() {
- super();
- }
-
- public BadFormatException(final String message) {
- super(message);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java
deleted file mode 100644
index 0fa72c3fd..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.widget.Button;
-import android.widget.FrameLayout;
-
-import com.android.inputmethod.latin.R;
-
-/**
- * A view that handles buttons inside it according to a status.
- */
-public class ButtonSwitcher extends FrameLayout {
- public static final int NOT_INITIALIZED = -1;
- public static final int STATUS_NO_BUTTON = 0;
- public static final int STATUS_INSTALL = 1;
- public static final int STATUS_CANCEL = 2;
- public static final int STATUS_DELETE = 3;
- // One of the above
- private int mStatus = NOT_INITIALIZED;
- private int mAnimateToStatus = NOT_INITIALIZED;
-
- // Animation directions
- public static final int ANIMATION_IN = 1;
- public static final int ANIMATION_OUT = 2;
-
- private Button mInstallButton;
- private Button mCancelButton;
- private Button mDeleteButton;
- private DictionaryListInterfaceState mInterfaceState;
- private OnClickListener mOnClickListener;
-
- public ButtonSwitcher(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ButtonSwitcher(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void reset(final DictionaryListInterfaceState interfaceState) {
- mStatus = NOT_INITIALIZED;
- mAnimateToStatus = NOT_INITIALIZED;
- mInterfaceState = interfaceState;
- }
-
- @Override
- protected void onLayout(final boolean changed, final int left, final int top, final int right,
- final int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mInstallButton = (Button)findViewById(R.id.dict_install_button);
- mCancelButton = (Button)findViewById(R.id.dict_cancel_button);
- mDeleteButton = (Button)findViewById(R.id.dict_delete_button);
- setInternalOnClickListener(mOnClickListener);
- setButtonPositionWithoutAnimation(mStatus);
- if (mAnimateToStatus != NOT_INITIALIZED) {
- // We have been asked to animate before we were ready, so we took a note of it.
- // We are now ready: launch the animation.
- animateButtonPosition(mStatus, mAnimateToStatus);
- mStatus = mAnimateToStatus;
- mAnimateToStatus = NOT_INITIALIZED;
- }
- }
-
- private Button getButton(final int status) {
- switch(status) {
- case STATUS_INSTALL:
- return mInstallButton;
- case STATUS_CANCEL:
- return mCancelButton;
- case STATUS_DELETE:
- return mDeleteButton;
- default:
- return null;
- }
- }
-
- public void setStatusAndUpdateVisuals(final int status) {
- if (mStatus == NOT_INITIALIZED) {
- setButtonPositionWithoutAnimation(status);
- mStatus = status;
- } else {
- if (null == mInstallButton) {
- // We may come here before we have been layout. In this case we don't know our
- // size yet so we can't start animations so we need to remember what animation to
- // start once layout has gone through.
- mAnimateToStatus = status;
- } else {
- animateButtonPosition(mStatus, status);
- mStatus = status;
- }
- }
- }
-
- private void setButtonPositionWithoutAnimation(final int status) {
- // This may be called by setStatus() before the layout has come yet.
- if (null == mInstallButton) return;
- final int width = getWidth();
- // Set to out of the screen if that's not the currently displayed status
- mInstallButton.setTranslationX(STATUS_INSTALL == status ? 0 : width);
- mCancelButton.setTranslationX(STATUS_CANCEL == status ? 0 : width);
- mDeleteButton.setTranslationX(STATUS_DELETE == status ? 0 : width);
- }
-
- // The helper method for {@link AnimatorListenerAdapter}.
- void animateButtonIfStatusIsEqual(final View newButton, final int newStatus) {
- if (newStatus != mStatus) return;
- animateButton(newButton, ANIMATION_IN);
- }
-
- private void animateButtonPosition(final int oldStatus, final int newStatus) {
- final View oldButton = getButton(oldStatus);
- final View newButton = getButton(newStatus);
- if (null != oldButton && null != newButton) {
- // Transition between two buttons : animate out, then in
- animateButton(oldButton, ANIMATION_OUT).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animation) {
- animateButtonIfStatusIsEqual(newButton, newStatus);
- }
- });
- } else if (null != oldButton) {
- animateButton(oldButton, ANIMATION_OUT);
- } else if (null != newButton) {
- animateButton(newButton, ANIMATION_IN);
- }
- }
-
- public void setInternalOnClickListener(final OnClickListener listener) {
- mOnClickListener = listener;
- if (null != mInstallButton) {
- // Already laid out : do it now
- mInstallButton.setOnClickListener(mOnClickListener);
- mCancelButton.setOnClickListener(mOnClickListener);
- mDeleteButton.setOnClickListener(mOnClickListener);
- }
- }
-
- private ViewPropertyAnimator animateButton(final View button, final int direction) {
- final float outerX = getWidth();
- final float innerX = button.getX() - button.getTranslationX();
- mInterfaceState.removeFromCache((View)getParent());
- if (ANIMATION_IN == direction) {
- button.setClickable(true);
- return button.animate().translationX(0);
- }
- button.setClickable(false);
- return button.animate().translationX(outerX - innerX);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java b/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java
deleted file mode 100644
index 3d0e29ed0..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-public final class CommonPreferences {
- private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
-
- public static SharedPreferences getCommonPreferences(final Context context) {
- return context.getSharedPreferences(COMMON_PREFERENCES_NAME, 0);
- }
-
- public static void enable(final SharedPreferences pref, final String id) {
- final SharedPreferences.Editor editor = pref.edit();
- editor.putBoolean(id, true);
- editor.apply();
- }
-
- public static void disable(final SharedPreferences pref, final String id) {
- final SharedPreferences.Editor editor = pref.edit();
- editor.putBoolean(id, false);
- editor.apply();
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/CompletedDownloadInfo.java b/java/src/com/android/inputmethod/dictionarypack/CompletedDownloadInfo.java
deleted file mode 100644
index ff198756e..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/CompletedDownloadInfo.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.DownloadManager;
-
-/**
- * Struct class to encapsulate the result of a completed download.
- */
-public class CompletedDownloadInfo {
- final String mUri;
- final long mDownloadId;
- final int mStatus;
- public CompletedDownloadInfo(final String uri, final long downloadId, final int status) {
- mUri = uri;
- mDownloadId = downloadId;
- mStatus = status;
- }
- public boolean wasSuccessful() {
- return DownloadManager.STATUS_SUCCESSFUL == mStatus;
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
deleted file mode 100644
index 759852025..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/**
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.DownloadManager;
-import android.app.DownloadManager.Query;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ProgressBar;
-
-public class DictionaryDownloadProgressBar extends ProgressBar {
- private static final String TAG = DictionaryDownloadProgressBar.class.getSimpleName();
- private static final int NOT_A_DOWNLOADMANAGER_PENDING_ID = 0;
-
- private String mClientId;
- private String mWordlistId;
- private boolean mIsCurrentlyAttachedToWindow = false;
- private Thread mReporterThread = null;
-
- public DictionaryDownloadProgressBar(final Context context) {
- super(context);
- }
-
- public DictionaryDownloadProgressBar(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setIds(final String clientId, final String wordlistId) {
- mClientId = clientId;
- mWordlistId = wordlistId;
- }
-
- static private int getDownloadManagerPendingIdFromWordlistId(final Context context,
- final String clientId, final String wordlistId) {
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
- final ContentValues wordlistValues =
- MetadataDbHelper.getContentValuesOfLatestAvailableWordlistById(db, wordlistId);
- if (null == wordlistValues) {
- // We don't know anything about a word list with this id. Bug? This should never
- // happen, but still return to prevent a crash.
- Log.e(TAG, "Unexpected word list ID: " + wordlistId);
- return NOT_A_DOWNLOADMANAGER_PENDING_ID;
- }
- return wordlistValues.getAsInteger(MetadataDbHelper.PENDINGID_COLUMN);
- }
-
- /*
- * This method will stop any running updater thread for this progress bar and create and run
- * a new one only if the progress bar is visible.
- * Hence, as a result of calling this method, the progress bar will have an updater thread
- * running if and only if the progress bar is visible.
- */
- private void updateReporterThreadRunningStatusAccordingToVisibility() {
- if (null != mReporterThread) mReporterThread.interrupt();
- if (mIsCurrentlyAttachedToWindow && View.VISIBLE == getVisibility()) {
- final int downloadManagerPendingId =
- getDownloadManagerPendingIdFromWordlistId(getContext(), mClientId, mWordlistId);
- if (NOT_A_DOWNLOADMANAGER_PENDING_ID == downloadManagerPendingId) {
- // Can't get the ID. This is never supposed to happen, but still clear the updater
- // thread and return to avoid a crash.
- mReporterThread = null;
- return;
- }
- final UpdaterThread updaterThread =
- new UpdaterThread(getContext(), downloadManagerPendingId);
- updaterThread.start();
- mReporterThread = updaterThread;
- } else {
- // We're not going to restart the thread anyway, so we may as well garbage collect it.
- mReporterThread = null;
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- mIsCurrentlyAttachedToWindow = true;
- updateReporterThreadRunningStatusAccordingToVisibility();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mIsCurrentlyAttachedToWindow = false;
- updateReporterThreadRunningStatusAccordingToVisibility();
- }
-
- private class UpdaterThread extends Thread {
- private final static int REPORT_PERIOD = 150; // how often to report progress, in ms
- final DownloadManagerWrapper mDownloadManagerWrapper;
- final int mId;
- public UpdaterThread(final Context context, final int id) {
- super();
- mDownloadManagerWrapper = new DownloadManagerWrapper(context);
- mId = id;
- }
- @Override
- public void run() {
- try {
- final UpdateHelper updateHelper = new UpdateHelper();
- final Query query = new Query().setFilterById(mId);
- setIndeterminate(true);
- while (!isInterrupted()) {
- final Cursor cursor = mDownloadManagerWrapper.query(query);
- if (null == cursor) {
- // Can't contact DownloadManager: this should never happen.
- return;
- }
- try {
- if (cursor.moveToNext()) {
- final int columnBytesDownloadedSoFar = cursor.getColumnIndex(
- DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
- final int bytesDownloadedSoFar =
- cursor.getInt(columnBytesDownloadedSoFar);
- updateHelper.setProgressFromAnotherThread(bytesDownloadedSoFar);
- } else {
- // Download has finished and DownloadManager has already been asked to
- // clean up the db entry.
- updateHelper.setProgressFromAnotherThread(getMax());
- return;
- }
- } finally {
- cursor.close();
- }
- Thread.sleep(REPORT_PERIOD);
- }
- } catch (InterruptedException e) {
- // Do nothing and terminate normally.
- }
- }
-
- class UpdateHelper implements Runnable {
- private int mProgress;
- @Override
- public void run() {
- setIndeterminate(false);
- setProgress(mProgress);
- }
- public void setProgressFromAnotherThread(final int progress) {
- if (mProgress != progress) {
- mProgress = progress;
- // For some unknown reason, setProgress just does not work from a separate
- // thread, although the code in ProgressBar looks like it should. Thus, we
- // resort to a runnable posted to the handler of the view.
- final Handler handler = getHandler();
- // It's possible to come here before this view has been laid out. If so,
- // just ignore the call - it will be updated again later.
- if (null == handler) return;
- handler.post(this);
- }
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java
deleted file mode 100644
index 836340a75..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * Helper class to maintain the interface state of word list preferences.
- *
- * This is necessary because the views are created on-demand by calling code. There are many
- * situations where views are renewed with little relation with user interaction. For example,
- * when scrolling, the view is reused so it doesn't keep its state, which means we need to keep
- * it separately. Also whenever the underlying dictionary list undergoes a change (for example,
- * update the metadata, or finish downloading) the whole list has to be thrown out and recreated
- * in case some dictionaries appeared, disappeared, changed states etc.
- */
-public class DictionaryListInterfaceState {
- static class State {
- public boolean mOpen = false;
- public int mStatus = MetadataDbHelper.STATUS_UNKNOWN;
- }
-
- private HashMap<String, State> mWordlistToState = new HashMap<>();
- private ArrayList<View> mViewCache = new ArrayList<>();
-
- public boolean isOpen(final String wordlistId) {
- final State state = mWordlistToState.get(wordlistId);
- if (null == state) return false;
- return state.mOpen;
- }
-
- public int getStatus(final String wordlistId) {
- final State state = mWordlistToState.get(wordlistId);
- if (null == state) return MetadataDbHelper.STATUS_UNKNOWN;
- return state.mStatus;
- }
-
- public void setOpen(final String wordlistId, final int status) {
- final State newState;
- final State state = mWordlistToState.get(wordlistId);
- newState = null == state ? new State() : state;
- newState.mOpen = true;
- newState.mStatus = status;
- mWordlistToState.put(wordlistId, newState);
- }
-
- public void closeAll() {
- for (final State state : mWordlistToState.values()) {
- state.mOpen = false;
- }
- }
-
- public View findFirstOrphanedView() {
- for (final View v : mViewCache) {
- if (null == v.getParent()) return v;
- }
- return null;
- }
-
- public View addToCacheAndReturnView(final View view) {
- mViewCache.add(view);
- return view;
- }
-
- public void removeFromCache(final View view) {
- mViewCache.remove(view);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java
deleted file mode 100644
index 13caea403..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-/**
- * A class to group constants for dictionary pack usage.
- *
- * This class only defines constants. It should not make any references to outside code as far as
- * possible, as it's used to separate cleanly the keyboard code from the dictionary pack code; this
- * is needed in particular to cleanly compile regression tests.
- */
-public class DictionaryPackConstants {
- /**
- * The root domain for the dictionary pack, upon which authorities and actions will append
- * their own distinctive strings.
- */
- private static final String DICTIONARY_DOMAIN = "com.android.inputmethod.dictionarypack.aosp";
-
- /**
- * Authority for the ContentProvider protocol.
- */
- // TODO: find some way to factorize this string with the one in the resources
- public static final String AUTHORITY = DICTIONARY_DOMAIN;
-
- /**
- * The action of the intent for publishing that new dictionary data is available.
- */
- // TODO: make this different across different packages. A suggested course of action is
- // to use the package name inside this string.
- // NOTE: The appended string should be uppercase like all other actions, but it's not for
- // historical reasons.
- public static final String NEW_DICTIONARY_INTENT_ACTION = DICTIONARY_DOMAIN + ".newdict";
-
- /**
- * The action of the intent sent by the dictionary pack to ask for a client to make
- * itself known. This is used when the settings activity is brought up for a client the
- * dictionary pack does not know about.
- */
- public static final String UNKNOWN_DICTIONARY_PROVIDER_CLIENT = DICTIONARY_DOMAIN
- + ".UNKNOWN_CLIENT";
-
- // In the above intents, the name of the string extra that contains the name of the client
- // we want information about.
- public static final String DICTIONARY_PROVIDER_CLIENT_EXTRA = "client";
-
- /**
- * The action of the intent to tell the dictionary provider to update now.
- */
- public static final String UPDATE_NOW_INTENT_ACTION = DICTIONARY_DOMAIN
- + ".UPDATE_NOW";
-
- /**
- * The intent action to inform the dictionary provider to initialize the db
- * and update now.
- */
- public static final String INIT_AND_UPDATE_NOW_INTENT_ACTION = DICTIONARY_DOMAIN
- + ".INIT_AND_UPDATE_NOW";
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
deleted file mode 100644
index 308b123e1..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ /dev/null
@@ -1,541 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.UriMatcher;
-import android.content.res.AssetFileDescriptor;
-import android.database.AbstractCursor;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.utils.DebugLogUtils;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-
-/**
- * Provider for dictionaries.
- *
- * This class is a ContentProvider exposing all available dictionary data as managed by
- * the dictionary pack.
- */
-public final class DictionaryProvider extends ContentProvider {
- private static final String TAG = DictionaryProvider.class.getSimpleName();
- public static final boolean DEBUG = false;
-
- public static final Uri CONTENT_URI =
- Uri.parse(ContentResolver.SCHEME_CONTENT + "://" + DictionaryPackConstants.AUTHORITY);
- private static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt";
- private static final String QUERY_PARAMETER_TRUE = "true";
- private static final String QUERY_PARAMETER_DELETE_RESULT = "result";
- private static final String QUERY_PARAMETER_FAILURE = "failure";
- public static final String QUERY_PARAMETER_PROTOCOL_VERSION = "protocol";
- private static final int NO_MATCH = 0;
- private static final int DICTIONARY_V1_WHOLE_LIST = 1;
- private static final int DICTIONARY_V1_DICT_INFO = 2;
- private static final int DICTIONARY_V2_METADATA = 3;
- private static final int DICTIONARY_V2_WHOLE_LIST = 4;
- private static final int DICTIONARY_V2_DICT_INFO = 5;
- private static final int DICTIONARY_V2_DATAFILE = 6;
- private static final UriMatcher sUriMatcherV1 = new UriMatcher(NO_MATCH);
- private static final UriMatcher sUriMatcherV2 = new UriMatcher(NO_MATCH);
- static
- {
- sUriMatcherV1.addURI(DictionaryPackConstants.AUTHORITY, "list", DICTIONARY_V1_WHOLE_LIST);
- sUriMatcherV1.addURI(DictionaryPackConstants.AUTHORITY, "*", DICTIONARY_V1_DICT_INFO);
- sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/metadata",
- DICTIONARY_V2_METADATA);
- sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/list", DICTIONARY_V2_WHOLE_LIST);
- sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/dict/*",
- DICTIONARY_V2_DICT_INFO);
- sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/datafile/*",
- DICTIONARY_V2_DATAFILE);
- }
-
- // MIME types for dictionary and dictionary list, as required by ContentProvider contract.
- public static final String DICT_LIST_MIME_TYPE =
- "vnd.android.cursor.item/vnd.google.dictionarylist";
- public static final String DICT_DATAFILE_MIME_TYPE =
- "vnd.android.cursor.item/vnd.google.dictionary";
-
- public static final String ID_CATEGORY_SEPARATOR = ":";
-
- private static final class WordListInfo {
- public final String mId;
- public final String mLocale;
- public final String mRawChecksum;
- public final int mMatchLevel;
- public WordListInfo(final String id, final String locale, final String rawChecksum,
- final int matchLevel) {
- mId = id;
- mLocale = locale;
- mRawChecksum = rawChecksum;
- mMatchLevel = matchLevel;
- }
- }
-
- /**
- * A cursor for returning a list of file ids from a List of strings.
- *
- * This simulates only the necessary methods. It has no error handling to speak of,
- * and does not support everything a database does, only a few select necessary methods.
- */
- private static final class ResourcePathCursor extends AbstractCursor {
-
- // Column names for the cursor returned by this content provider.
- static private final String[] columnNames = { MetadataDbHelper.WORDLISTID_COLUMN,
- MetadataDbHelper.LOCALE_COLUMN, MetadataDbHelper.RAW_CHECKSUM_COLUMN };
-
- // The list of word lists served by this provider that match the client request.
- final WordListInfo[] mWordLists;
- // Note : the cursor also uses mPos, which is defined in AbstractCursor.
-
- public ResourcePathCursor(final Collection<WordListInfo> wordLists) {
- // Allocating a 0-size WordListInfo here allows the toArray() method
- // to ensure we have a strongly-typed array. It's thrown out. That's
- // what the documentation of #toArray says to do in order to get a
- // new strongly typed array of the correct size.
- mWordLists = wordLists.toArray(new WordListInfo[0]);
- mPos = 0;
- }
-
- @Override
- public String[] getColumnNames() {
- return columnNames;
- }
-
- @Override
- public int getCount() {
- return mWordLists.length;
- }
-
- @Override public double getDouble(int column) { return 0; }
- @Override public float getFloat(int column) { return 0; }
- @Override public int getInt(int column) { return 0; }
- @Override public short getShort(int column) { return 0; }
- @Override public long getLong(int column) { return 0; }
-
- @Override public String getString(final int column) {
- switch (column) {
- case 0: return mWordLists[mPos].mId;
- case 1: return mWordLists[mPos].mLocale;
- case 2: return mWordLists[mPos].mRawChecksum;
- default : return null;
- }
- }
-
- @Override
- public boolean isNull(final int column) {
- if (mPos >= mWordLists.length) return true;
- return column != 0;
- }
- }
-
- @Override
- public boolean onCreate() {
- return true;
- }
-
- private static int matchUri(final Uri uri) {
- int protocolVersion = 1;
- final String protocolVersionArg = uri.getQueryParameter(QUERY_PARAMETER_PROTOCOL_VERSION);
- if ("2".equals(protocolVersionArg)) protocolVersion = 2;
- switch (protocolVersion) {
- case 1: return sUriMatcherV1.match(uri);
- case 2: return sUriMatcherV2.match(uri);
- default: return NO_MATCH;
- }
- }
-
- private static String getClientId(final Uri uri) {
- int protocolVersion = 1;
- final String protocolVersionArg = uri.getQueryParameter(QUERY_PARAMETER_PROTOCOL_VERSION);
- if ("2".equals(protocolVersionArg)) protocolVersion = 2;
- switch (protocolVersion) {
- case 1: return null; // In protocol 1, the client ID is always null.
- case 2: return uri.getPathSegments().get(0);
- default: return null;
- }
- }
-
- /**
- * Returns the MIME type of the content associated with an Uri
- *
- * @see android.content.ContentProvider#getType(android.net.Uri)
- *
- * @param uri the URI of the content the type of which should be returned.
- * @return the MIME type, or null if the URL is not recognized.
- */
- @Override
- public String getType(final Uri uri) {
- PrivateLog.log("Asked for type of : " + uri);
- final int match = matchUri(uri);
- switch (match) {
- case NO_MATCH: return null;
- case DICTIONARY_V1_WHOLE_LIST:
- case DICTIONARY_V1_DICT_INFO:
- case DICTIONARY_V2_WHOLE_LIST:
- case DICTIONARY_V2_DICT_INFO: return DICT_LIST_MIME_TYPE;
- case DICTIONARY_V2_DATAFILE: return DICT_DATAFILE_MIME_TYPE;
- default: return null;
- }
- }
-
- /**
- * Query the provider for dictionary files.
- *
- * This version dispatches the query according to the protocol version found in the
- * ?protocol= query parameter. If absent or not well-formed, it defaults to 1.
- * @see android.content.ContentProvider#query(Uri, String[], String, String[], String)
- *
- * @param uri a content uri (see sUriMatcherV{1,2} at the top of this file for format)
- * @param projection ignored. All columns are always returned.
- * @param selection ignored.
- * @param selectionArgs ignored.
- * @param sortOrder ignored. The results are always returned in no particular order.
- * @return a cursor matching the uri, or null if the URI was not recognized.
- */
- @Override
- public Cursor query(final Uri uri, final String[] projection, final String selection,
- final String[] selectionArgs, final String sortOrder) {
- DebugLogUtils.l("Uri =", uri);
- PrivateLog.log("Query : " + uri);
- final String clientId = getClientId(uri);
- final int match = matchUri(uri);
- switch (match) {
- case DICTIONARY_V1_WHOLE_LIST:
- case DICTIONARY_V2_WHOLE_LIST:
- final Cursor c = MetadataDbHelper.queryDictionaries(getContext(), clientId);
- DebugLogUtils.l("List of dictionaries with count", c.getCount());
- PrivateLog.log("Returned a list of " + c.getCount() + " items");
- return c;
- case DICTIONARY_V2_DICT_INFO:
- // In protocol version 2, we return null if the client is unknown. Otherwise
- // we behave exactly like for protocol 1.
- if (!MetadataDbHelper.isClientKnown(getContext(), clientId)) return null;
- // Fall through
- case DICTIONARY_V1_DICT_INFO:
- final String locale = uri.getLastPathSegment();
- final Collection<WordListInfo> dictFiles =
- getDictionaryWordListsForLocale(clientId, locale);
- // TODO: pass clientId to the following function
- DictionaryService.updateNowIfNotUpdatedInAVeryLongTime(getContext());
- if (null != dictFiles && dictFiles.size() > 0) {
- PrivateLog.log("Returned " + dictFiles.size() + " files");
- return new ResourcePathCursor(dictFiles);
- }
- PrivateLog.log("No dictionary files for this URL");
- return new ResourcePathCursor(Collections.<WordListInfo>emptyList());
- // V2_METADATA and V2_DATAFILE are not supported for query()
- default:
- return null;
- }
- }
-
- /**
- * Helper method to get the wordlist metadata associated with a wordlist ID.
- *
- * @param clientId the ID of the client
- * @param wordlistId the ID of the wordlist for which to get the metadata.
- * @return the metadata for this wordlist ID, or null if none could be found.
- */
- private ContentValues getWordlistMetadataForWordlistId(final String clientId,
- final String wordlistId) {
- final Context context = getContext();
- if (TextUtils.isEmpty(wordlistId)) return null;
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
- return MetadataDbHelper.getInstalledOrDeletingWordListContentValuesByWordListId(
- db, wordlistId);
- }
-
- /**
- * Opens an asset file for an URI.
- *
- * Called by {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} or
- * {@link android.content.ContentResolver#openInputStream(Uri)} from a client requesting a
- * dictionary.
- * @see android.content.ContentProvider#openAssetFile(Uri, String)
- *
- * @param uri the URI the file is for.
- * @param mode the mode to read the file. MUST be "r" for readonly.
- * @return the descriptor, or null if the file is not found or if mode is not equals to "r".
- */
- @Override
- public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) {
- if (null == mode || !"r".equals(mode)) return null;
-
- final int match = matchUri(uri);
- if (DICTIONARY_V1_DICT_INFO != match && DICTIONARY_V2_DATAFILE != match) {
- // Unsupported URI for openAssetFile
- Log.w(TAG, "Unsupported URI for openAssetFile : " + uri);
- return null;
- }
- final String wordlistId = uri.getLastPathSegment();
- final String clientId = getClientId(uri);
- final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId);
-
- if (null == wordList) return null;
-
- try {
- final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_DELETING == status) {
- // This will return an empty file (R.raw.empty points at an empty dictionary)
- // This is how we "delete" the files. It allows Android Keyboard to fake deleting
- // a default dictionary - which is actually in its assets and can't be really
- // deleted.
- final AssetFileDescriptor afd = getContext().getResources().openRawResourceFd(
- R.raw.empty);
- return afd;
- }
- final String localFilename =
- wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
- final File f = getContext().getFileStreamPath(localFilename);
- final ParcelFileDescriptor pfd =
- ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
- return new AssetFileDescriptor(pfd, 0, pfd.getStatSize());
- } catch (FileNotFoundException e) {
- // No file : fall through and return null
- }
- return null;
- }
-
- /**
- * Reads the metadata and returns the collection of dictionaries for a given locale.
- *
- * Word list IDs are expected to be in the form category:manual_id. This method
- * will select only one word list for each category: the one with the most specific
- * locale matching the locale specified in the URI. The manual id serves only to
- * distinguish a word list from another for the purpose of updating, and is arbitrary
- * but may not contain a colon.
- *
- * @param clientId the ID of the client requesting the list
- * @param locale the locale for which we want the list, as a String
- * @return a collection of ids. It is guaranteed to be non-null, but may be empty.
- */
- private Collection<WordListInfo> getDictionaryWordListsForLocale(final String clientId,
- final String locale) {
- final Context context = getContext();
- final Cursor results =
- MetadataDbHelper.queryInstalledOrDeletingOrAvailableDictionaryMetadata(context,
- clientId);
- if (null == results) {
- return Collections.<WordListInfo>emptyList();
- }
- try {
- final HashMap<String, WordListInfo> dicts = new HashMap<>();
- final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
- final int localeIndex = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
- final int localFileNameIndex =
- results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
- final int rawChecksumIndex =
- results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN);
- final int statusIndex = results.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
- if (results.moveToFirst()) {
- do {
- final String wordListId = results.getString(idIndex);
- if (TextUtils.isEmpty(wordListId)) continue;
- final String[] wordListIdArray =
- TextUtils.split(wordListId, ID_CATEGORY_SEPARATOR);
- final String wordListCategory;
- if (2 == wordListIdArray.length) {
- // This is at the category:manual_id format.
- wordListCategory = wordListIdArray[0];
- // We don't need to read wordListIdArray[1] here, because it's irrelevant to
- // word list selection - it's just a name we use to identify which data file
- // is a newer version of which word list. We do however return the full id
- // string for each selected word list, so in this sense we are 'using' it.
- } else {
- // This does not contain a colon, like the old format does. Old-format IDs
- // always point to main dictionaries, so we force the main category upon it.
- wordListCategory = UpdateHandler.MAIN_DICTIONARY_CATEGORY;
- }
- final String wordListLocale = results.getString(localeIndex);
- final String wordListLocalFilename = results.getString(localFileNameIndex);
- final String wordListRawChecksum = results.getString(rawChecksumIndex);
- final int wordListStatus = results.getInt(statusIndex);
- // Test the requested locale against this wordlist locale. The requested locale
- // has to either match exactly or be more specific than the dictionary - a
- // dictionary for "en" would match both a request for "en" or for "en_US", but a
- // dictionary for "en_GB" would not match a request for "en_US". Thus if all
- // three of "en" "en_US" and "en_GB" dictionaries are installed, a request for
- // "en_US" would match "en" and "en_US", and a request for "en" only would only
- // match the generic "en" dictionary. For more details, see the documentation
- // for LocaleUtils#getMatchLevel.
- final int matchLevel = LocaleUtils.getMatchLevel(wordListLocale, locale);
- if (!LocaleUtils.isMatch(matchLevel)) {
- // The locale of this wordlist does not match the required locale.
- // Skip this wordlist and go to the next.
- continue;
- }
- if (MetadataDbHelper.STATUS_INSTALLED == wordListStatus) {
- // If the file does not exist, it has been deleted and the IME should
- // already have it. Do not return it. However, this only applies if the
- // word list is INSTALLED, for if it is DELETING we should return it always
- // so that Android Keyboard can perform the actual deletion.
- final File f = getContext().getFileStreamPath(wordListLocalFilename);
- if (!f.isFile()) {
- continue;
- }
- } else if (MetadataDbHelper.STATUS_AVAILABLE == wordListStatus) {
- // The locale is the id for the main dictionary.
- UpdateHandler.installIfNeverRequested(context, clientId, wordListId);
- continue;
- }
- final WordListInfo currentBestMatch = dicts.get(wordListCategory);
- if (null == currentBestMatch
- || currentBestMatch.mMatchLevel < matchLevel) {
- dicts.put(wordListCategory, new WordListInfo(wordListId, wordListLocale,
- wordListRawChecksum, matchLevel));
- }
- } while (results.moveToNext());
- }
- return Collections.unmodifiableCollection(dicts.values());
- } finally {
- results.close();
- }
- }
-
- /**
- * Deletes the file pointed by Uri, as returned by openAssetFile.
- *
- * @param uri the URI the file is for.
- * @param selection ignored
- * @param selectionArgs ignored
- * @return the number of files deleted (0 or 1 in the current implementation)
- * @see android.content.ContentProvider#delete(Uri, String, String[])
- */
- @Override
- public int delete(final Uri uri, final String selection, final String[] selectionArgs)
- throws UnsupportedOperationException {
- final int match = matchUri(uri);
- if (DICTIONARY_V1_DICT_INFO == match || DICTIONARY_V2_DATAFILE == match) {
- return deleteDataFile(uri);
- }
- if (DICTIONARY_V2_METADATA == match) {
- if (MetadataDbHelper.deleteClient(getContext(), getClientId(uri))) {
- return 1;
- }
- return 0;
- }
- // Unsupported URI for delete
- return 0;
- }
-
- private int deleteDataFile(final Uri uri) {
- final String wordlistId = uri.getLastPathSegment();
- final String clientId = getClientId(uri);
- final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId);
- if (null == wordList) {
- return 0;
- }
- final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- final int version = wordList.getAsInteger(MetadataDbHelper.VERSION_COLUMN);
- if (MetadataDbHelper.STATUS_DELETING == status) {
- UpdateHandler.markAsDeleted(getContext(), clientId, wordlistId, version, status);
- return 1;
- }
- if (MetadataDbHelper.STATUS_INSTALLED == status) {
- final String result = uri.getQueryParameter(QUERY_PARAMETER_DELETE_RESULT);
- if (QUERY_PARAMETER_FAILURE.equals(result)) {
- if (DEBUG) {
- Log.d(TAG,
- "Dictionary is broken, attempting to retry download & installation.");
- }
- UpdateHandler.markAsBrokenOrRetrying(getContext(), clientId, wordlistId, version);
- }
- final String localFilename =
- wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
- final File f = getContext().getFileStreamPath(localFilename);
- // f.delete() returns true if the file was successfully deleted, false otherwise
- return f.delete() ? 1 : 0;
- }
- Log.e(TAG, "Attempt to delete a file whose status is " + status);
- return 0;
- }
-
- /**
- * Insert data into the provider. May be either a metadata source URL or some dictionary info.
- *
- * @param uri the designated content URI. See sUriMatcherV{1,2} for available URIs.
- * @param values the values to insert for this content uri
- * @return the URI for the newly inserted item. May be null if arguments don't allow for insert
- */
- @Override
- public Uri insert(final Uri uri, final ContentValues values)
- throws UnsupportedOperationException {
- if (null == uri || null == values) return null; // Should never happen but let's be safe
- PrivateLog.log("Insert, uri = " + uri.toString());
- final String clientId = getClientId(uri);
- switch (matchUri(uri)) {
- case DICTIONARY_V2_METADATA:
- // The values should contain a valid client ID and a valid URI for the metadata.
- // The client ID may not be null, nor may it be empty because the empty client ID
- // is reserved for internal use.
- // The metadata URI may not be null, but it may be empty if the client does not
- // want the dictionary pack to update the metadata automatically.
- MetadataDbHelper.updateClientInfo(getContext(), clientId, values);
- break;
- case DICTIONARY_V2_DICT_INFO:
- try {
- final WordListMetadata newDictionaryMetadata =
- WordListMetadata.createFromContentValues(
- MetadataDbHelper.completeWithDefaultValues(values));
- new ActionBatch.MarkPreInstalledAction(clientId, newDictionaryMetadata)
- .execute(getContext());
- } catch (final BadFormatException e) {
- Log.w(TAG, "Not enough information to insert this dictionary " + values, e);
- }
- // We just received new information about the list of dictionary for this client.
- // For all intents and purposes, this is new metadata, so we should publish it
- // so that any listeners (like the Settings interface for example) can update
- // themselves.
- UpdateHandler.publishUpdateMetadataCompleted(getContext(), true);
- break;
- case DICTIONARY_V1_WHOLE_LIST:
- case DICTIONARY_V1_DICT_INFO:
- PrivateLog.log("Attempt to insert : " + uri);
- throw new UnsupportedOperationException(
- "Insertion in the dictionary is not supported in this version");
- }
- return uri;
- }
-
- /**
- * Updating data is not supported, and will throw an exception.
- * @see android.content.ContentProvider#update(Uri, ContentValues, String, String[])
- * @see android.content.ContentProvider#insert(Uri, ContentValues)
- */
- @Override
- public int update(final Uri uri, final ContentValues values, final String selection,
- final String[] selectionArgs) throws UnsupportedOperationException {
- PrivateLog.log("Attempt to update : " + uri);
- throw new UnsupportedOperationException("Updating dictionary words is not supported");
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
deleted file mode 100644
index 5ab55bc44..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.android.inputmethod.latin.BinaryDictionaryFileDumper;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import java.util.Locale;
-import java.util.Random;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-
-/**
- * Service that handles background tasks for the dictionary provider.
- *
- * This service provides the context for the long-running operations done by the
- * dictionary provider. Those include:
- * - Checking for the last update date and scheduling the next update. This runs every
- * day around midnight, upon reception of the DATE_CHANGED_INTENT_ACTION broadcast.
- * Every four days, it schedules an update of the metadata with the alarm manager.
- * - Issuing the order to update the metadata. This runs every four days, between 0 and
- * 6, upon reception of the UPDATE_NOW_INTENT_ACTION broadcast sent by the alarm manager
- * as a result of the above action.
- * - Handling a download that just ended. These come in two flavors:
- * - Metadata is finished downloading. We should check whether there are new dictionaries
- * available, and download those that we need that have new versions.
- * - A dictionary file finished downloading. We should put the file ready for a client IME
- * to access, and mark the current state as such.
- */
-public final class DictionaryService extends Service {
- private static final String TAG = DictionaryService.class.getSimpleName();
-
- /**
- * The package name, to use in the intent actions.
- */
- private static final String PACKAGE_NAME = "com.android.inputmethod.latin";
-
- /**
- * The action of the date changing, used to schedule a periodic freshness check
- */
- private static final String DATE_CHANGED_INTENT_ACTION =
- Intent.ACTION_DATE_CHANGED;
-
- /**
- * The action of displaying a toast to warn the user an automatic download is starting.
- */
- /* package */ static final String SHOW_DOWNLOAD_TOAST_INTENT_ACTION =
- PACKAGE_NAME + ".SHOW_DOWNLOAD_TOAST_INTENT_ACTION";
-
- /**
- * A locale argument, as a String.
- */
- /* package */ static final String LOCALE_INTENT_ARGUMENT = "locale";
-
- /**
- * How often, in milliseconds, we want to update the metadata. This is a
- * floor value; actually, it may happen several hours later, or even more.
- */
- private static final long UPDATE_FREQUENCY_MILLIS = TimeUnit.DAYS.toMillis(4);
-
- /**
- * We are waked around midnight, local time. We want to wake between midnight and 6 am,
- * roughly. So use a random time between 0 and this delay.
- */
- private static final int MAX_ALARM_DELAY_MILLIS = (int)TimeUnit.HOURS.toMillis(6);
-
- /**
- * How long we consider a "very long time". If no update took place in this time,
- * the content provider will trigger an update in the background.
- */
- private static final long VERY_LONG_TIME_MILLIS = TimeUnit.DAYS.toMillis(14);
-
- /**
- * After starting a download, how long we wait before considering it may be stuck. After this
- * period is elapsed, if the keyboard tries to download again, then we cancel and re-register
- * the request; if it's within this time, we just leave it be.
- * It's important to note that we do not re-submit the request merely because the time is up.
- * This is only to decide whether to cancel the old one and re-requesting when the keyboard
- * fires a new request for the same data.
- */
- public static final long NO_CANCEL_DOWNLOAD_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(30);
-
- /**
- * An executor that serializes tasks given to it.
- */
- private ThreadPoolExecutor mExecutor;
- private static final int WORKER_THREAD_TIMEOUT_SECONDS = 15;
-
- @Override
- public void onCreate() {
- // By default, a thread pool executor does not timeout its core threads, so it will
- // never kill them when there isn't any work to do any more. That would mean the service
- // can never die! By creating it this way and calling allowCoreThreadTimeOut, we allow
- // the single thread to time out after WORKER_THREAD_TIMEOUT_SECONDS = 15 seconds, allowing
- // the process to be reclaimed by the system any time after that if it's not doing
- // anything else.
- // Executors#newSingleThreadExecutor creates a ThreadPoolExecutor but it returns the
- // superclass ExecutorService which does not have the #allowCoreThreadTimeOut method,
- // so we can't use that.
- mExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
- WORKER_THREAD_TIMEOUT_SECONDS /* keepAliveTime */,
- TimeUnit.SECONDS /* unit for keepAliveTime */,
- new LinkedBlockingQueue<Runnable>() /* workQueue */);
- mExecutor.allowCoreThreadTimeOut(true);
- }
-
- @Override
- public void onDestroy() {
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // This service cannot be bound
- return null;
- }
-
- /**
- * Executes an explicit command.
- *
- * This is the entry point for arbitrary commands that are executed upon reception of certain
- * events that should be executed on the context of this service. The supported commands are:
- * - Check last update time and possibly schedule an update of the data for later.
- * This is triggered every day, upon reception of the DATE_CHANGED_INTENT_ACTION broadcast.
- * - Update data NOW.
- * This is normally received upon trigger of the scheduled update.
- * - Handle a finished download.
- * This executes the actions that must be taken after a file (metadata or dictionary data
- * has been downloaded (or failed to download).
- * The commands that can be spun an another thread will be executed serially, in order, on
- * a worker thread that is created on demand and terminates after a short while if there isn't
- * any work left to do.
- */
- @Override
- public synchronized int onStartCommand(final Intent intent, final int flags,
- final int startId) {
- final DictionaryService self = this;
- if (SHOW_DOWNLOAD_TOAST_INTENT_ACTION.equals(intent.getAction())) {
- final String localeString = intent.getStringExtra(LOCALE_INTENT_ARGUMENT);
- if (localeString == null) {
- Log.e(TAG, "Received " + intent.getAction() + " without locale; skipped");
- } else {
- // This is a UI action, it can't be run in another thread
- showStartDownloadingToast(
- this, LocaleUtils.constructLocaleFromString(localeString));
- }
- } else {
- // If it's a command that does not require UI, arrange for the work to be done on a
- // separate thread, so that we can return right away. The executor will spawn a thread
- // if necessary, or reuse a thread that has become idle as appropriate.
- // DATE_CHANGED or UPDATE_NOW are examples of commands that can be done on another
- // thread.
- mExecutor.submit(new Runnable() {
- @Override
- public void run() {
- dispatchBroadcast(self, intent);
- // Since calls to onStartCommand are serialized, the submissions to the executor
- // are serialized. That means we are guaranteed to call the stopSelfResult()
- // in the same order that we got them, so we don't need to take care of the
- // order.
- stopSelfResult(startId);
- }
- });
- }
- return Service.START_REDELIVER_INTENT;
- }
-
- static void dispatchBroadcast(final Context context, final Intent intent) {
- final String action = intent.getAction();
- if (DATE_CHANGED_INTENT_ACTION.equals(action)) {
- // This happens when the date of the device changes. This normally happens
- // at midnight local time, but it may happen if the user changes the date
- // by hand or something similar happens.
- checkTimeAndMaybeSetupUpdateAlarm(context);
- } else if (DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION.equals(action)) {
- // Intent to trigger an update now.
- UpdateHandler.tryUpdate(context);
- } else if (DictionaryPackConstants.INIT_AND_UPDATE_NOW_INTENT_ACTION.equals(action)) {
- // Initialize the client Db.
- final String mClientId = context.getString(R.string.dictionary_pack_client_id);
- BinaryDictionaryFileDumper.initializeClientRecordHelper(context, mClientId);
-
- // Updates the metadata and the download the dictionaries.
- UpdateHandler.tryUpdate(context);
- } else {
- UpdateHandler.downloadFinished(context, intent);
- }
- }
-
- /**
- * Setups an alarm to check for updates if an update is due.
- */
- private static void checkTimeAndMaybeSetupUpdateAlarm(final Context context) {
- // Of all clients, if the one that hasn't been updated for the longest
- // is still more recent than UPDATE_FREQUENCY_MILLIS, do nothing.
- if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY_MILLIS)) return;
-
- PrivateLog.log("Date changed - registering alarm");
- AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
-
- // Best effort to wake between midnight and MAX_ALARM_DELAY_MILLIS in the morning.
- // It doesn't matter too much if this is very inexact.
- final long now = System.currentTimeMillis();
- final long alarmTime = now + new Random().nextInt(MAX_ALARM_DELAY_MILLIS);
- final Intent updateIntent = new Intent(DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION);
- // Set the package name to ensure the PendingIntent is only delivered to trusted components
- updateIntent.setPackage(context.getPackageName());
- int pendingIntentFlags = PendingIntent.FLAG_CANCEL_CURRENT;
- if (android.os.Build.VERSION.SDK_INT >= 23) {
- pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE;
- }
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
- updateIntent, pendingIntentFlags);
-
- // We set the alarm in the type that doesn't forcefully wake the device
- // from sleep, but fires the next time the device actually wakes for any
- // other reason.
- if (null != alarmManager) alarmManager.set(AlarmManager.RTC, alarmTime, pendingIntent);
- }
-
- /**
- * Utility method to decide whether the last update is older than a certain time.
- *
- * @return true if at least `time' milliseconds have elapsed since last update, false otherwise.
- */
- private static boolean isLastUpdateAtLeastThisOld(final Context context, final long time) {
- final long now = System.currentTimeMillis();
- final long lastUpdate = MetadataDbHelper.getOldestUpdateTime(context);
- PrivateLog.log("Last update was " + lastUpdate);
- return lastUpdate + time < now;
- }
-
- /**
- * Refreshes data if it hasn't been refreshed in a very long time.
- *
- * This will check the last update time, and if it's been more than VERY_LONG_TIME_MILLIS,
- * update metadata now - and possibly take subsequent update actions.
- */
- public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) {
- if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME_MILLIS)) return;
- UpdateHandler.tryUpdate(context);
- }
-
- /**
- * Shows a toast informing the user that an automatic dictionary download is starting.
- */
- private static void showStartDownloadingToast(final Context context,
- @Nonnull final Locale locale) {
- final String toastText = String.format(
- context.getString(R.string.toast_downloading_suggestions),
- locale.getDisplayName());
- Toast.makeText(context, toastText, Toast.LENGTH_LONG).show();
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
deleted file mode 100644
index 284032beb..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import com.android.inputmethod.latin.utils.FragmentUtils;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-
-/**
- * Preference screen.
- */
-public final class DictionarySettingsActivity extends PreferenceActivity {
- private static final String DEFAULT_FRAGMENT = DictionarySettingsFragment.class.getName();
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public Intent getIntent() {
- final Intent modIntent = new Intent(super.getIntent());
- modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT);
- modIntent.putExtra(EXTRA_NO_HEADERS, true);
- // Important note : the original intent should contain a String extra with the key
- // DictionarySettingsFragment.DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT so that the
- // fragment can know who the client is.
- return modIntent;
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- @Override
- public boolean isValidFragment(String fragmentName) {
- return FragmentUtils.isValidFragment(fragmentName);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
deleted file mode 100644
index 35b46a978..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceGroup;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-
-import com.android.inputmethod.latin.R;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Locale;
-import java.util.TreeMap;
-
-/**
- * Preference screen.
- */
-public final class DictionarySettingsFragment extends PreferenceFragment
- implements UpdateHandler.UpdateEventListener {
- private static final String TAG = DictionarySettingsFragment.class.getSimpleName();
-
- static final private String DICT_LIST_ID = "list";
- static final public String DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT = "clientId";
-
- static final private int MENU_UPDATE_NOW = Menu.FIRST;
-
- private View mLoadingView;
- private String mClientId;
- private ConnectivityManager mConnectivityManager;
- private MenuItem mUpdateNowMenu;
- private boolean mChangedSettings;
- private DictionaryListInterfaceState mDictionaryListInterfaceState =
- new DictionaryListInterfaceState();
- // never null
- private TreeMap<String, WordListPreference> mCurrentPreferenceMap = new TreeMap<>();
-
- private final BroadcastReceiver mConnectivityChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- refreshNetworkState();
- }
- };
-
- /**
- * Empty constructor for fragment generation.
- */
- public DictionarySettingsFragment() {
- }
-
- @Override
- public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
- final Bundle savedInstanceState) {
- final View v = inflater.inflate(R.layout.loading_page, container, true);
- mLoadingView = v.findViewById(R.id.loading_container);
- return super.onCreateView(inflater, container, savedInstanceState);
- }
-
- @Override
- public void onActivityCreated(final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- final Activity activity = getActivity();
- mClientId = activity.getIntent().getStringExtra(DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT);
- mConnectivityManager =
- (ConnectivityManager)activity.getSystemService(Context.CONNECTIVITY_SERVICE);
- addPreferencesFromResource(R.xml.dictionary_settings);
- refreshInterface();
- setHasOptionsMenu(true);
- }
-
- @Override
- public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
- new AsyncTask<Void, Void, String>() {
- @Override
- protected String doInBackground(Void... params) {
- return MetadataDbHelper.getMetadataUriAsString(getActivity(), mClientId);
- }
-
- @Override
- protected void onPostExecute(String metadataUri) {
- // We only add the "Refresh" button if we have a non-empty URL to refresh from. If
- // the URL is empty, of course we can't refresh so it makes no sense to display
- // this.
- if (!TextUtils.isEmpty(metadataUri)) {
- if (mUpdateNowMenu == null) {
- mUpdateNowMenu = menu.add(Menu.NONE, MENU_UPDATE_NOW, 0,
- R.string.check_for_updates_now);
- mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- }
- refreshNetworkState();
- }
- }
- }.execute();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mChangedSettings = false;
- UpdateHandler.registerUpdateEventListener(this);
- final Activity activity = getActivity();
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- getActivity().registerReceiver(mConnectivityChangedReceiver, filter);
- refreshNetworkState();
-
- new Thread("onResume") {
- @Override
- public void run() {
- if (!MetadataDbHelper.isClientKnown(activity, mClientId)) {
- Log.i(TAG, "Unknown dictionary pack client: " + mClientId
- + ". Requesting info.");
- final Intent unknownClientBroadcast =
- new Intent(DictionaryPackConstants.UNKNOWN_DICTIONARY_PROVIDER_CLIENT);
- unknownClientBroadcast.putExtra(
- DictionaryPackConstants.DICTIONARY_PROVIDER_CLIENT_EXTRA, mClientId);
- activity.sendBroadcast(unknownClientBroadcast);
- }
- }
- }.start();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- final Activity activity = getActivity();
- UpdateHandler.unregisterUpdateEventListener(this);
- activity.unregisterReceiver(mConnectivityChangedReceiver);
- if (mChangedSettings) {
- final Intent newDictBroadcast =
- new Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
- activity.sendBroadcast(newDictBroadcast);
- mChangedSettings = false;
- }
- }
-
- @Override
- public void downloadedMetadata(final boolean succeeded) {
- stopLoadingAnimation();
- if (!succeeded) return; // If the download failed nothing changed, so no need to refresh
- new Thread("refreshInterface") {
- @Override
- public void run() {
- refreshInterface();
- }
- }.start();
- }
-
- @Override
- public void wordListDownloadFinished(final String wordListId, final boolean succeeded) {
- final WordListPreference pref = findWordListPreference(wordListId);
- if (null == pref) return;
- // TODO: Report to the user if !succeeded
- final Activity activity = getActivity();
- if (null == activity) return;
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- // We have to re-read the db in case the description has changed, and to
- // find out what state it ended up if the download wasn't successful
- // TODO: don't redo everything, only re-read and set this word list status
- refreshInterface();
- }
- });
- }
-
- private WordListPreference findWordListPreference(final String id) {
- final PreferenceGroup prefScreen = getPreferenceScreen();
- if (null == prefScreen) {
- Log.e(TAG, "Could not find the preference group");
- return null;
- }
- for (int i = prefScreen.getPreferenceCount() - 1; i >= 0; --i) {
- final Preference pref = prefScreen.getPreference(i);
- if (pref instanceof WordListPreference) {
- final WordListPreference wlPref = (WordListPreference)pref;
- if (id.equals(wlPref.mWordlistId)) {
- return wlPref;
- }
- }
- }
- Log.e(TAG, "Could not find the preference for a word list id " + id);
- return null;
- }
-
- @Override
- public void updateCycleCompleted() {}
-
- void refreshNetworkState() {
- NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
- boolean isConnected = null == info ? false : info.isConnected();
- if (null != mUpdateNowMenu) mUpdateNowMenu.setEnabled(isConnected);
- }
-
- void refreshInterface() {
- final Activity activity = getActivity();
- if (null == activity) return;
- final PreferenceGroup prefScreen = getPreferenceScreen();
- final Collection<? extends Preference> prefList =
- createInstalledDictSettingsCollection(mClientId);
-
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- // TODO: display this somewhere
- // if (0 != lastUpdate) mUpdateNowPreference.setSummary(updateNowSummary);
- refreshNetworkState();
-
- removeAnyDictSettings(prefScreen);
- int i = 0;
- for (Preference preference : prefList) {
- preference.setOrder(i++);
- prefScreen.addPreference(preference);
- }
- }
- });
- }
-
- private static Preference createErrorMessage(final Activity activity, final int messageResource) {
- final Preference message = new Preference(activity);
- message.setTitle(messageResource);
- message.setEnabled(false);
- return message;
- }
-
- static void removeAnyDictSettings(final PreferenceGroup prefGroup) {
- for (int i = prefGroup.getPreferenceCount() - 1; i >= 0; --i) {
- prefGroup.removePreference(prefGroup.getPreference(i));
- }
- }
-
- /**
- * Creates a WordListPreference list to be added to the screen.
- *
- * This method only creates the preferences but does not add them.
- * Thus, it can be called on another thread.
- *
- * @param clientId the id of the client for which we want to display the dictionary list
- * @return A collection of preferences ready to add to the interface.
- */
- private Collection<? extends Preference> createInstalledDictSettingsCollection(
- final String clientId) {
- // This will directly contact the DictionaryProvider and request the list exactly like
- // any regular client would do.
- // Considering the respective value of the respective constants used here for each path,
- // segment, the url generated by this is of the form (assuming "clientId" as a clientId)
- // content://com.android.inputmethod.latin.dictionarypack/clientId/list?procotol=2
- final Uri contentUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(getString(R.string.authority))
- .appendPath(clientId)
- .appendPath(DICT_LIST_ID)
- // Need to use version 2 to get this client's list
- .appendQueryParameter(DictionaryProvider.QUERY_PARAMETER_PROTOCOL_VERSION, "2")
- .build();
- final Activity activity = getActivity();
- final Cursor cursor = (null == activity) ? null
- : activity.getContentResolver().query(contentUri, null, null, null, null);
-
- if (null == cursor) {
- final ArrayList<Preference> result = new ArrayList<>();
- result.add(createErrorMessage(activity, R.string.cannot_connect_to_dict_service));
- return result;
- }
- try {
- if (!cursor.moveToFirst()) {
- final ArrayList<Preference> result = new ArrayList<>();
- result.add(createErrorMessage(activity, R.string.no_dictionaries_available));
- return result;
- }
- final String systemLocaleString = Locale.getDefault().toString();
- final TreeMap<String, WordListPreference> prefMap = new TreeMap<>();
- final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
- final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
- final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
- final int descriptionIndex = cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
- final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
- final int filesizeIndex = cursor.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
- do {
- final String wordlistId = cursor.getString(idIndex);
- final int version = cursor.getInt(versionIndex);
- final String localeString = cursor.getString(localeIndex);
- final Locale locale = new Locale(localeString);
- final String description = cursor.getString(descriptionIndex);
- final int status = cursor.getInt(statusIndex);
- final int matchLevel = LocaleUtils.getMatchLevel(systemLocaleString, localeString);
- final String matchLevelString = LocaleUtils.getMatchLevelSortedString(matchLevel);
- final int filesize = cursor.getInt(filesizeIndex);
- // The key is sorted in lexicographic order, according to the match level, then
- // the description.
- final String key = matchLevelString + "." + description + "." + wordlistId;
- final WordListPreference existingPref = prefMap.get(key);
- if (null == existingPref || existingPref.hasPriorityOver(status)) {
- final WordListPreference oldPreference = mCurrentPreferenceMap.get(key);
- final WordListPreference pref;
- if (null != oldPreference
- && oldPreference.mVersion == version
- && oldPreference.hasStatus(status)
- && oldPreference.mLocale.equals(locale)) {
- // If the old preference has all the new attributes, reuse it. Ideally,
- // we should reuse the old pref even if its status is different and call
- // setStatus here, but setStatus calls Preference#setSummary() which
- // needs to be done on the UI thread and we're not on the UI thread
- // here. We could do all this work on the UI thread, but in this case
- // it's probably lighter to stay on a background thread and throw this
- // old preference out.
- pref = oldPreference;
- } else {
- // Otherwise, discard it and create a new one instead.
- // TODO: when the status is different from the old one, we need to
- // animate the old one out before animating the new one in.
- pref = new WordListPreference(activity, mDictionaryListInterfaceState,
- mClientId, wordlistId, version, locale, description, status,
- filesize);
- }
- prefMap.put(key, pref);
- }
- } while (cursor.moveToNext());
- mCurrentPreferenceMap = prefMap;
- return prefMap.values();
- } finally {
- cursor.close();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case MENU_UPDATE_NOW:
- if (View.GONE == mLoadingView.getVisibility()) {
- startRefresh();
- } else {
- cancelRefresh();
- }
- return true;
- }
- return false;
- }
-
- private void startRefresh() {
- startLoadingAnimation();
- mChangedSettings = true;
- UpdateHandler.registerUpdateEventListener(this);
- final Activity activity = getActivity();
- new Thread("updateByHand") {
- @Override
- public void run() {
- // We call tryUpdate(), which returns whether we could successfully start an update.
- // If we couldn't, we'll never receive the end callback, so we stop the loading
- // animation and return to the previous screen.
- if (!UpdateHandler.tryUpdate(activity)) {
- stopLoadingAnimation();
- }
- }
- }.start();
- }
-
- private void cancelRefresh() {
- UpdateHandler.unregisterUpdateEventListener(this);
- final Context context = getActivity();
- new Thread("cancelByHand") {
- @Override
- public void run() {
- UpdateHandler.cancelUpdate(context, mClientId);
- stopLoadingAnimation();
- }
- }.start();
- }
-
- private void startLoadingAnimation() {
- mLoadingView.setVisibility(View.VISIBLE);
- getView().setVisibility(View.GONE);
- // We come here when the menu element is pressed so presumably it can't be null. But
- // better safe than sorry.
- if (null != mUpdateNowMenu) mUpdateNowMenu.setTitle(R.string.cancel);
- }
-
- void stopLoadingAnimation() {
- final View preferenceView = getView();
- final Activity activity = getActivity();
- if (null == activity) return;
- final View loadingView = mLoadingView;
- final MenuItem updateNowMenu = mUpdateNowMenu;
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- loadingView.setVisibility(View.GONE);
- preferenceView.setVisibility(View.VISIBLE);
- loadingView.startAnimation(AnimationUtils.loadAnimation(
- activity, android.R.anim.fade_out));
- preferenceView.startAnimation(AnimationUtils.loadAnimation(
- activity, android.R.anim.fade_in));
- // The menu is created by the framework asynchronously after the activity,
- // which means it's possible to have the activity running but the menu not
- // created yet - hence the necessity for a null check here.
- if (null != updateNowMenu) {
- updateNowMenu.setTitle(R.string.check_for_updates_now);
- }
- }
- });
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java b/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java
deleted file mode 100644
index 6247a15e2..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-/**
- * A simple container of download ID and download start date.
- */
-public class DownloadIdAndStartDate {
- public final long mId;
- public final long mStartDate;
- public DownloadIdAndStartDate(final long id, final long startDate) {
- mId = id;
- mStartDate = startDate;
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
deleted file mode 100644
index e2e9a7e44..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.DownloadManager;
-import android.app.DownloadManager.Query;
-import android.app.DownloadManager.Request;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.FileNotFoundException;
-import java.util.Arrays;
-
-import javax.annotation.Nullable;
-
-/**
- * A class to help with calling DownloadManager methods.
- *
- * Mostly, the problem here is that most methods from DownloadManager may throw SQL exceptions if
- * they can't open the database on disk. We want to avoid crashing in these cases but can't do
- * much more, so this class insulates the callers from these. SQLiteException also inherit from
- * RuntimeException so they are unchecked :(
- * While we're at it, we also insulate callers from the cases where DownloadManager is disabled,
- * and getSystemService returns null.
- */
-public class DownloadManagerWrapper {
- private final static String TAG = DownloadManagerWrapper.class.getSimpleName();
- private final DownloadManager mDownloadManager;
-
- public DownloadManagerWrapper(final Context context) {
- this((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE));
- }
-
- private DownloadManagerWrapper(final DownloadManager downloadManager) {
- mDownloadManager = downloadManager;
- }
-
- public void remove(final long... ids) {
- try {
- if (null != mDownloadManager) {
- mDownloadManager.remove(ids);
- }
- } catch (IllegalArgumentException e) {
- // This is expected to happen on boot when the device is encrypted.
- } catch (SQLiteException e) {
- // We couldn't remove the file from DownloadManager. Apparently, the database can't
- // be opened. It may be a problem with file system corruption. In any case, there is
- // not much we can do apart from avoiding crashing.
- Log.e(TAG, "Can't remove files with ID " + Arrays.toString(ids) +
- " from download manager", e);
- }
- }
-
- public ParcelFileDescriptor openDownloadedFile(final long fileId) throws FileNotFoundException {
- try {
- if (null != mDownloadManager) {
- return mDownloadManager.openDownloadedFile(fileId);
- }
- } catch (IllegalArgumentException e) {
- // This is expected to happen on boot when the device is encrypted.
- } catch (SQLiteException e) {
- Log.e(TAG, "Can't open downloaded file with ID " + fileId, e);
- }
- // We come here if mDownloadManager is null or if an exception was thrown.
- throw new FileNotFoundException();
- }
-
- @Nullable
- public Cursor query(final Query query) {
- try {
- if (null != mDownloadManager) {
- return mDownloadManager.query(query);
- }
- } catch (IllegalArgumentException e) {
- // This is expected to happen on boot when the device is encrypted.
- } catch (SQLiteException e) {
- Log.e(TAG, "Can't query the download manager", e);
- }
- // We come here if mDownloadManager is null or if an exception was thrown.
- return null;
- }
-
- public long enqueue(final Request request) {
- try {
- if (null != mDownloadManager) {
- return mDownloadManager.enqueue(request);
- }
- } catch (IllegalArgumentException e) {
- // This is expected to happen on boot when the device is encrypted.
- } catch (SQLiteException e) {
- Log.e(TAG, "Can't enqueue a request with the download manager", e);
- }
- return 0;
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
deleted file mode 100644
index 908d931a0..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.Html;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import javax.annotation.Nullable;
-
-/**
- * This implements the dialog for asking the user whether it's okay to download dictionaries over
- * a metered connection or not (e.g. their mobile data plan).
- */
-public final class DownloadOverMeteredDialog extends Activity {
- final public static String CLIENT_ID_KEY = "client_id";
- final public static String WORDLIST_TO_DOWNLOAD_KEY = "wordlist_to_download";
- final public static String SIZE_KEY = "size";
- final public static String LOCALE_KEY = "locale";
- private String mClientId;
- private String mWordListToDownload;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Intent intent = getIntent();
- mClientId = intent.getStringExtra(CLIENT_ID_KEY);
- mWordListToDownload = intent.getStringExtra(WORDLIST_TO_DOWNLOAD_KEY);
- final String localeString = intent.getStringExtra(LOCALE_KEY);
- final long size = intent.getIntExtra(SIZE_KEY, 0);
- setContentView(R.layout.download_over_metered);
- setTexts(localeString, size);
- }
-
- private void setTexts(@Nullable final String localeString, final long size) {
- final String promptFormat = getString(R.string.should_download_over_metered_prompt);
- final String allowButtonFormat = getString(R.string.download_over_metered);
- final String language = (null == localeString) ? ""
- : LocaleUtils.constructLocaleFromString(localeString).getDisplayLanguage();
- final TextView prompt = (TextView)findViewById(R.id.download_over_metered_prompt);
- prompt.setText(Html.fromHtml(String.format(promptFormat, language)));
- final Button allowButton = (Button)findViewById(R.id.allow_button);
- allowButton.setText(String.format(allowButtonFormat, ((float)size)/(1024*1024)));
- }
-
- // This method is externally referenced from layout/download_over_metered.xml using onClick
- // attribute of Button.
- @ExternallyReferenced
- @SuppressWarnings("unused")
- public void onClickDeny(final View v) {
- UpdateHandler.setDownloadOverMeteredSetting(this, false);
- finish();
- }
-
- // This method is externally referenced from layout/download_over_metered.xml using onClick
- // attribute of Button.
- @ExternallyReferenced
- @SuppressWarnings("unused")
- public void onClickAllow(final View v) {
- UpdateHandler.setDownloadOverMeteredSetting(this, true);
- UpdateHandler.installIfNeverRequested(this, mClientId, mWordListToDownload);
- finish();
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadRecord.java b/java/src/com/android/inputmethod/dictionarypack/DownloadRecord.java
deleted file mode 100644
index c26299027..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadRecord.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.ContentValues;
-
-/**
- * Struct class to encapsulate a client ID with content values about a download.
- */
-public class DownloadRecord {
- public final String mClientId;
- // Only word lists have attributes, and the ContentValues should contain the same
- // keys as they do for all MetadataDbHelper functions. Since only word lists have
- // attributes, a null pointer here means this record represents metadata.
- public final ContentValues mAttributes;
- public DownloadRecord(final String clientId, final ContentValues attributes) {
- mClientId = clientId;
- mAttributes = attributes;
- }
- public boolean isMetadata() {
- return null == mAttributes;
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/dictionarypack/EventHandler.java b/java/src/com/android/inputmethod/dictionarypack/EventHandler.java
deleted file mode 100644
index 859f1b35b..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/EventHandler.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public final class EventHandler extends BroadcastReceiver {
- /**
- * Receives a intent broadcast.
- *
- * We receive every day a broadcast indicating that date changed.
- * Then we wait a random amount of time before actually registering
- * the download, to avoid concentrating too many accesses around
- * midnight in more populated timezones.
- * We receive all broadcasts here, so this can be either the DATE_CHANGED broadcast, the
- * UPDATE_NOW private broadcast that we receive when the time-randomizing alarm triggers
- * for regular update or from applications that want to test the dictionary pack, or a
- * broadcast from DownloadManager telling that a download has finished.
- * See inside of AndroidManifest.xml to see which events are caught.
- * Also @see {@link BroadcastReceiver#onReceive(Context, Intent)}
- *
- * @param context the context of the application.
- * @param intent the intent that was broadcast.
- */
- @Override
- public void onReceive(final Context context, final Intent intent) {
- intent.setClass(context, DictionaryService.class);
- context.startService(intent);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java b/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
deleted file mode 100644
index c9e128d70..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.util.Log;
-
-/**
- * A very simple problem reporter.
- */
-final class LogProblemReporter implements ProblemReporter {
- private final String TAG;
-
- public LogProblemReporter(final String tag) {
- TAG = tag;
- }
-
- @Override
- public void report(final Exception e) {
- Log.e(TAG, "Reporting problem", e);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java b/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java
deleted file mode 100644
index ccd054c84..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-
-public final class MD5Calculator {
- private MD5Calculator() {} // This helper class is not instantiable
-
- public static String checksum(final InputStream in) throws IOException {
- // This code from the Android documentation for MessageDigest. Nearly verbatim.
- MessageDigest digester;
- try {
- digester = MessageDigest.getInstance("MD5");
- } catch (java.security.NoSuchAlgorithmException e) {
- return null; // Platform does not support MD5 : can't check, so return null
- }
- final byte[] bytes = new byte[8192];
- int byteCount;
- while ((byteCount = in.read(bytes)) > 0) {
- digester.update(bytes, 0, byteCount);
- }
- final byte[] digest = digester.digest();
- final StringBuilder s = new StringBuilder();
- for (int i = 0; i < digest.length; ++i) {
- s.append(String.format("%1$02x", digest[i]));
- }
- return s.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
deleted file mode 100644
index 94dd7a1c9..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ /dev/null
@@ -1,1155 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.DebugLogUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.TreeMap;
-
-import javax.annotation.Nullable;
-
-/**
- * Various helper functions for the state database
- */
-public class MetadataDbHelper extends SQLiteOpenHelper {
- private static final String TAG = MetadataDbHelper.class.getSimpleName();
-
- // This was the initial release version of the database. It should never be
- // changed going forward.
- private static final int METADATA_DATABASE_INITIAL_VERSION = 3;
- // This is the first released version of the database that implements CLIENTID. It is
- // used to identify the versions for upgrades. This should never change going forward.
- private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 6;
- // The current database version.
- // This MUST be increased every time the dictionary pack metadata URL changes.
- private static final int CURRENT_METADATA_DATABASE_VERSION = 16;
-
- private final static long NOT_A_DOWNLOAD_ID = -1;
-
- // The number of retries allowed when attempting to download a broken dictionary.
- public static final int DICTIONARY_RETRY_THRESHOLD = 2;
-
- public static final String METADATA_TABLE_NAME = "pendingUpdates";
- static final String CLIENT_TABLE_NAME = "clients";
- public static final String PENDINGID_COLUMN = "pendingid"; // Download Manager ID
- public static final String TYPE_COLUMN = "type";
- public static final String STATUS_COLUMN = "status";
- public static final String LOCALE_COLUMN = "locale";
- public static final String WORDLISTID_COLUMN = "id";
- public static final String DESCRIPTION_COLUMN = "description";
- public static final String LOCAL_FILENAME_COLUMN = "filename";
- public static final String REMOTE_FILENAME_COLUMN = "url";
- public static final String DATE_COLUMN = "date";
- public static final String CHECKSUM_COLUMN = "checksum";
- public static final String FILESIZE_COLUMN = "filesize";
- public static final String VERSION_COLUMN = "version";
- public static final String FORMATVERSION_COLUMN = "formatversion";
- public static final String FLAGS_COLUMN = "flags";
- public static final String RAW_CHECKSUM_COLUMN = "rawChecksum";
- public static final String RETRY_COUNT_COLUMN = "remainingRetries";
- public static final int COLUMN_COUNT = 15;
-
- private static final String CLIENT_CLIENT_ID_COLUMN = "clientid";
- private static final String CLIENT_METADATA_URI_COLUMN = "uri";
- private static final String CLIENT_METADATA_ADDITIONAL_ID_COLUMN = "additionalid";
- private static final String CLIENT_LAST_UPDATE_DATE_COLUMN = "lastupdate";
- private static final String CLIENT_PENDINGID_COLUMN = "pendingid"; // Download Manager ID
-
- public static final String METADATA_DATABASE_NAME_STEM = "pendingUpdates";
- public static final String METADATA_UPDATE_DESCRIPTION = "metadata";
-
- public static final String DICTIONARIES_ASSETS_PATH = "dictionaries";
-
- // Statuses, for storing in the STATUS_COLUMN
- // IMPORTANT: The following are used as index arrays in ../WordListPreference
- // Do not change their values without updating the matched code.
- // Unknown status: this should never happen.
- public static final int STATUS_UNKNOWN = 0;
- // Available: this word list is available, but it is not downloaded (not downloading), because
- // it is set not to be used.
- public static final int STATUS_AVAILABLE = 1;
- // Downloading: this word list is being downloaded.
- public static final int STATUS_DOWNLOADING = 2;
- // Installed: this word list is installed and usable.
- public static final int STATUS_INSTALLED = 3;
- // Disabled: this word list is installed, but has been disabled by the user.
- public static final int STATUS_DISABLED = 4;
- // Deleting: the user marked this word list to be deleted, but it has not been yet because
- // Latin IME is not up yet.
- public static final int STATUS_DELETING = 5;
- // Retry: dictionary got corrupted, so an attempt must be done to download & install it again.
- public static final int STATUS_RETRYING = 6;
-
- // Types, for storing in the TYPE_COLUMN
- // This is metadata about what is available.
- public static final int TYPE_METADATA = 1;
- // This is a bulk file. It should replace older files.
- public static final int TYPE_BULK = 2;
- // This is an incremental update, expected to be small, and meaningless on its own.
- public static final int TYPE_UPDATE = 3;
-
- private static final String METADATA_TABLE_CREATE =
- "CREATE TABLE " + METADATA_TABLE_NAME + " ("
- + PENDINGID_COLUMN + " INTEGER, "
- + TYPE_COLUMN + " INTEGER, "
- + STATUS_COLUMN + " INTEGER, "
- + WORDLISTID_COLUMN + " TEXT, "
- + LOCALE_COLUMN + " TEXT, "
- + DESCRIPTION_COLUMN + " TEXT, "
- + LOCAL_FILENAME_COLUMN + " TEXT, "
- + REMOTE_FILENAME_COLUMN + " TEXT, "
- + DATE_COLUMN + " INTEGER, "
- + CHECKSUM_COLUMN + " TEXT, "
- + FILESIZE_COLUMN + " INTEGER, "
- + VERSION_COLUMN + " INTEGER,"
- + FORMATVERSION_COLUMN + " INTEGER, "
- + FLAGS_COLUMN + " INTEGER, "
- + RAW_CHECKSUM_COLUMN + " TEXT,"
- + RETRY_COUNT_COLUMN + " INTEGER, "
- + "PRIMARY KEY (" + WORDLISTID_COLUMN + "," + VERSION_COLUMN + "));";
- private static final String METADATA_CREATE_CLIENT_TABLE =
- "CREATE TABLE IF NOT EXISTS " + CLIENT_TABLE_NAME + " ("
- + CLIENT_CLIENT_ID_COLUMN + " TEXT, "
- + CLIENT_METADATA_URI_COLUMN + " TEXT, "
- + CLIENT_METADATA_ADDITIONAL_ID_COLUMN + " TEXT, "
- + CLIENT_LAST_UPDATE_DATE_COLUMN + " INTEGER NOT NULL DEFAULT 0, "
- + CLIENT_PENDINGID_COLUMN + " INTEGER, "
- + FLAGS_COLUMN + " INTEGER, "
- + "PRIMARY KEY (" + CLIENT_CLIENT_ID_COLUMN + "));";
-
- // List of all metadata table columns.
- static final String[] METADATA_TABLE_COLUMNS = { PENDINGID_COLUMN, TYPE_COLUMN,
- STATUS_COLUMN, WORDLISTID_COLUMN, LOCALE_COLUMN, DESCRIPTION_COLUMN,
- LOCAL_FILENAME_COLUMN, REMOTE_FILENAME_COLUMN, DATE_COLUMN, CHECKSUM_COLUMN,
- FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN,
- RAW_CHECKSUM_COLUMN, RETRY_COUNT_COLUMN };
- // List of all client table columns.
- static final String[] CLIENT_TABLE_COLUMNS = { CLIENT_CLIENT_ID_COLUMN,
- CLIENT_METADATA_URI_COLUMN, CLIENT_PENDINGID_COLUMN, FLAGS_COLUMN };
- // List of public columns returned to clients. Everything that is not in this list is
- // private and implementation-dependent.
- static final String[] DICTIONARIES_LIST_PUBLIC_COLUMNS = { STATUS_COLUMN, WORDLISTID_COLUMN,
- LOCALE_COLUMN, DESCRIPTION_COLUMN, DATE_COLUMN, FILESIZE_COLUMN, VERSION_COLUMN };
-
- // This class exhibits a singleton-like behavior by client ID, so it is getInstance'd
- // and has a private c'tor.
- private static TreeMap<String, MetadataDbHelper> sInstanceMap = null;
- public static synchronized MetadataDbHelper getInstance(final Context context,
- final String clientIdOrNull) {
- // As a backward compatibility feature, null can be passed here to retrieve the "default"
- // database. Before multi-client support, the dictionary packed used only one database
- // and would not be able to handle several dictionary sets. Passing null here retrieves
- // this legacy database. New clients should make sure to always pass a client ID so as
- // to avoid conflicts.
- final String clientId = null != clientIdOrNull ? clientIdOrNull : "";
- if (null == sInstanceMap) sInstanceMap = new TreeMap<>();
- MetadataDbHelper helper = sInstanceMap.get(clientId);
- if (null == helper) {
- helper = new MetadataDbHelper(context, clientId);
- sInstanceMap.put(clientId, helper);
- }
- return helper;
- }
- private MetadataDbHelper(final Context context, final String clientId) {
- super(context,
- METADATA_DATABASE_NAME_STEM + (TextUtils.isEmpty(clientId) ? "" : "." + clientId),
- null, CURRENT_METADATA_DATABASE_VERSION);
- mContext = context;
- mClientId = clientId;
- }
-
- private final Context mContext;
- private final String mClientId;
-
- /**
- * Get the database itself. This always returns the same object for any client ID. If the
- * client ID is null, a default database is returned for backward compatibility. Don't
- * pass null for new calls.
- *
- * @param context the context to create the database from. This is ignored after the first call.
- * @param clientId the client id to retrieve the database of. null for default (deprecated)
- * @return the database.
- */
- public static SQLiteDatabase getDb(final Context context, final String clientId) {
- return getInstance(context, clientId).getWritableDatabase();
- }
-
- private void createClientTable(final SQLiteDatabase db) {
- // The clients table only exists in the primary db, the one that has an empty client id
- if (!TextUtils.isEmpty(mClientId)) return;
- db.execSQL(METADATA_CREATE_CLIENT_TABLE);
- final String defaultMetadataUri = mContext.getString(R.string.default_metadata_uri);
- if (!TextUtils.isEmpty(defaultMetadataUri)) {
- final ContentValues defaultMetadataValues = new ContentValues();
- defaultMetadataValues.put(CLIENT_CLIENT_ID_COLUMN, "");
- defaultMetadataValues.put(CLIENT_METADATA_URI_COLUMN, defaultMetadataUri);
- defaultMetadataValues.put(CLIENT_PENDINGID_COLUMN, UpdateHandler.NOT_AN_ID);
- db.insert(CLIENT_TABLE_NAME, null, defaultMetadataValues);
- }
- }
-
- /**
- * Create the table and populate it with the resources found inside the apk.
- *
- * @see SQLiteOpenHelper#onCreate(SQLiteDatabase)
- *
- * @param db the database to create and populate.
- */
- @Override
- public void onCreate(final SQLiteDatabase db) {
- db.execSQL(METADATA_TABLE_CREATE);
- createClientTable(db);
- }
-
- private static void addRawChecksumColumnUnlessPresent(final SQLiteDatabase db) {
- try {
- db.execSQL("SELECT " + RAW_CHECKSUM_COLUMN + " FROM "
- + METADATA_TABLE_NAME + " LIMIT 0;");
- } catch (SQLiteException e) {
- Log.i(TAG, "No " + RAW_CHECKSUM_COLUMN + " column : creating it");
- db.execSQL("ALTER TABLE " + METADATA_TABLE_NAME + " ADD COLUMN "
- + RAW_CHECKSUM_COLUMN + " TEXT;");
- }
- }
-
- private static void addRetryCountColumnUnlessPresent(final SQLiteDatabase db) {
- try {
- db.execSQL("SELECT " + RETRY_COUNT_COLUMN + " FROM "
- + METADATA_TABLE_NAME + " LIMIT 0;");
- } catch (SQLiteException e) {
- Log.i(TAG, "No " + RETRY_COUNT_COLUMN + " column : creating it");
- db.execSQL("ALTER TABLE " + METADATA_TABLE_NAME + " ADD COLUMN "
- + RETRY_COUNT_COLUMN + " INTEGER DEFAULT " + DICTIONARY_RETRY_THRESHOLD + ";");
- }
- }
-
- /**
- * Upgrade the database. Upgrade from version 3 is supported.
- * Version 3 has a DB named METADATA_DATABASE_NAME_STEM containing a table METADATA_TABLE_NAME.
- * Version 6 and above has a DB named METADATA_DATABASE_NAME_STEM containing a
- * table CLIENT_TABLE_NAME, and for each client a table called METADATA_TABLE_STEM + "." + the
- * name of the client and contains a table METADATA_TABLE_NAME.
- * For schemas, see the above create statements. The schemas have never changed so far.
- *
- * This method is called by the framework. See {@link SQLiteOpenHelper#onUpgrade}
- * @param db The database we are upgrading
- * @param oldVersion The old database version (the one on the disk)
- * @param newVersion The new database version as supplied to the constructor of SQLiteOpenHelper
- */
- @Override
- public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
- if (METADATA_DATABASE_INITIAL_VERSION == oldVersion
- && METADATA_DATABASE_VERSION_WITH_CLIENTID <= newVersion
- && CURRENT_METADATA_DATABASE_VERSION >= newVersion) {
- // Upgrade from version METADATA_DATABASE_INITIAL_VERSION to version
- // METADATA_DATABASE_VERSION_WITH_CLIENT_ID
- // Only the default database should contain the client table, so we test for mClientId.
- if (TextUtils.isEmpty(mClientId)) {
- // Anyway in version 3 only the default table existed so the emptiness
- // test should always be true, but better check to be sure.
- createClientTable(db);
- }
- } else if (METADATA_DATABASE_VERSION_WITH_CLIENTID < newVersion
- && CURRENT_METADATA_DATABASE_VERSION >= newVersion) {
- // Here we drop the client table, so that all clients send us their information again.
- // The client table contains the URL to hit to update the available dictionaries list,
- // but the info about the dictionaries themselves is stored in the table called
- // METADATA_TABLE_NAME and we want to keep it, so we only drop the client table.
- db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME);
- // Only the default database should contain the client table, so we test for mClientId.
- if (TextUtils.isEmpty(mClientId)) {
- createClientTable(db);
- }
- } else {
- // If we're not in the above case, either we are upgrading from an earlier versionCode
- // and we should wipe the database, or we are handling a version we never heard about
- // (can only be a bug) so it's safer to wipe the database.
- db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME);
- db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME);
- onCreate(db);
- }
- // A rawChecksum column that did not exist in the previous versions was added that
- // corresponds to the md5 checksum of the file after decompression/decryption. This is to
- // strengthen the system against corrupted dictionary files.
- // The most secure way to upgrade a database is to just test for the column presence, and
- // add it if it's not there.
- addRawChecksumColumnUnlessPresent(db);
-
- // A retry count column that did not exist in the previous versions was added that
- // corresponds to the number of download & installation attempts that have been made
- // in order to strengthen the system recovery from corrupted dictionary files.
- // The most secure way to upgrade a database is to just test for the column presence, and
- // add it if it's not there.
- addRetryCountColumnUnlessPresent(db);
- }
-
- /**
- * Downgrade the database. This drops and recreates the table in all cases.
- */
- @Override
- public void onDowngrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
- // No matter what the numerical values of oldVersion and newVersion are, we know this
- // is a downgrade (newVersion < oldVersion). There is no way to know what the future
- // databases will look like, but we know it's extremely likely that it's okay to just
- // drop the tables and start from scratch. Hence, we ignore the versions and just wipe
- // everything we want to use.
- if (oldVersion <= newVersion) {
- Log.e(TAG, "onDowngrade database but new version is higher? " + oldVersion + " <= "
- + newVersion);
- }
- db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME);
- db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME);
- onCreate(db);
- }
-
- /**
- * Given a client ID, returns whether this client exists.
- *
- * @param context a context to open the database
- * @param clientId the client ID to check
- * @return true if the client is known, false otherwise
- */
- public static boolean isClientKnown(final Context context, final String clientId) {
- // If the client is known, they'll have a non-null metadata URI. An empty string is
- // allowed as a metadata URI, if the client doesn't want any updates to happen.
- return null != getMetadataUriAsString(context, clientId);
- }
-
- private static final MetadataUriGetter sMetadataUriGetter = new MetadataUriGetter();
-
- /**
- * Returns the metadata URI as a string.
- *
- * If the client is not known, this will return null. If it is known, it will return
- * the URI as a string. Note that the empty string is a valid value.
- *
- * @param context a context instance to open the database on
- * @param clientId the ID of the client we want the metadata URI of
- * @return the string representation of the URI
- */
- public static String getMetadataUriAsString(final Context context, final String clientId) {
- SQLiteDatabase defaultDb = MetadataDbHelper.getDb(context, null);
- final Cursor cursor = defaultDb.query(MetadataDbHelper.CLIENT_TABLE_NAME,
- new String[] { MetadataDbHelper.CLIENT_METADATA_URI_COLUMN },
- MetadataDbHelper.CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId },
- null, null, null, null);
- try {
- if (!cursor.moveToFirst()) return null;
- return sMetadataUriGetter.getUri(context, cursor.getString(0));
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Update the last metadata update time for all clients using a particular URI.
- *
- * This method searches for all clients using a particular URI and updates the last
- * update time for this client.
- * The current time is used as the latest update time. This saved date will be what
- * is returned henceforth by {@link #getLastUpdateDateForClient(Context, String)},
- * until this method is called again.
- *
- * @param context a context instance to open the database on
- * @param uri the metadata URI we just downloaded
- */
- public static void saveLastUpdateTimeOfUri(final Context context, final String uri) {
- PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis());
- final ContentValues values = new ContentValues();
- values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis());
- final SQLiteDatabase defaultDb = getDb(context, null);
- final Cursor cursor = MetadataDbHelper.queryClientIds(context);
- if (null == cursor) return;
- try {
- if (!cursor.moveToFirst()) return;
- do {
- final String clientId = cursor.getString(0);
- final String metadataUri =
- MetadataDbHelper.getMetadataUriAsString(context, clientId);
- if (metadataUri.equals(uri)) {
- defaultDb.update(CLIENT_TABLE_NAME, values,
- CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId });
- }
- } while (cursor.moveToNext());
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Retrieves the last date at which we updated the metadata for this client.
- *
- * The returned date is in milliseconds from the EPOCH; this is the same unit as
- * returned by {@link System#currentTimeMillis()}.
- *
- * @param context a context instance to open the database on
- * @param clientId the client ID to get the latest update date of
- * @return the last date at which this client was updated, as a long.
- */
- public static long getLastUpdateDateForClient(final Context context, final String clientId) {
- SQLiteDatabase defaultDb = getDb(context, null);
- final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
- new String[] { CLIENT_LAST_UPDATE_DATE_COLUMN },
- CLIENT_CLIENT_ID_COLUMN + " = ?",
- new String[] { null == clientId ? "" : clientId },
- null, null, null, null);
- try {
- if (!cursor.moveToFirst()) return 0;
- return cursor.getLong(0); // Only one column, return it
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Get the metadata download ID for a metadata URI.
- *
- * This will retrieve the download ID for the metadata file that has the passed URI.
- * If this URI is not being downloaded right now, it will return NOT_AN_ID.
- *
- * @param context a context instance to open the database on
- * @param uri the URI to retrieve the metadata download ID of
- * @return the download id and start date, or null if the URL is not known
- */
- public static DownloadIdAndStartDate getMetadataDownloadIdAndStartDateForURI(
- final Context context, final String uri) {
- SQLiteDatabase defaultDb = getDb(context, null);
- final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
- new String[] { CLIENT_PENDINGID_COLUMN, CLIENT_LAST_UPDATE_DATE_COLUMN },
- CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri },
- null, null, null, null);
- try {
- if (!cursor.moveToFirst()) return null;
- return new DownloadIdAndStartDate(cursor.getInt(0), cursor.getLong(1));
- } finally {
- cursor.close();
- }
- }
-
- public static long getOldestUpdateTime(final Context context) {
- SQLiteDatabase defaultDb = getDb(context, null);
- final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
- new String[] { CLIENT_LAST_UPDATE_DATE_COLUMN },
- null, null, null, null, null);
- try {
- if (!cursor.moveToFirst()) return 0;
- final int columnIndex = 0; // Only one column queried
- // Initialize the earliestTime to the largest possible value.
- long earliestTime = Long.MAX_VALUE; // Almost 300 million years in the future
- do {
- final long thisTime = cursor.getLong(columnIndex);
- earliestTime = Math.min(thisTime, earliestTime);
- } while (cursor.moveToNext());
- return earliestTime;
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Helper method to make content values to write into the database.
- * @return content values with all the arguments put with the right column names.
- */
- public static ContentValues makeContentValues(final int pendingId, final int type,
- final int status, final String wordlistId, final String locale,
- final String description, final String filename, final String url, final long date,
- final String rawChecksum, final String checksum, final int retryCount,
- final long filesize, final int version, final int formatVersion) {
- final ContentValues result = new ContentValues(COLUMN_COUNT);
- result.put(PENDINGID_COLUMN, pendingId);
- result.put(TYPE_COLUMN, type);
- result.put(WORDLISTID_COLUMN, wordlistId);
- result.put(STATUS_COLUMN, status);
- result.put(LOCALE_COLUMN, locale);
- result.put(DESCRIPTION_COLUMN, description);
- result.put(LOCAL_FILENAME_COLUMN, filename);
- result.put(REMOTE_FILENAME_COLUMN, url);
- result.put(DATE_COLUMN, date);
- result.put(RAW_CHECKSUM_COLUMN, rawChecksum);
- result.put(RETRY_COUNT_COLUMN, retryCount);
- result.put(CHECKSUM_COLUMN, checksum);
- result.put(FILESIZE_COLUMN, filesize);
- result.put(VERSION_COLUMN, version);
- result.put(FORMATVERSION_COLUMN, formatVersion);
- result.put(FLAGS_COLUMN, 0);
- return result;
- }
-
- /**
- * Helper method to fill in an incomplete ContentValues with default values.
- * A wordlist ID and a locale are required, otherwise BadFormatException is thrown.
- * @return the same object that was passed in, completed with default values.
- */
- public static ContentValues completeWithDefaultValues(final ContentValues result)
- throws BadFormatException {
- if (null == result.get(WORDLISTID_COLUMN) || null == result.get(LOCALE_COLUMN)) {
- throw new BadFormatException();
- }
- // 0 for the pending id, because there is none
- if (null == result.get(PENDINGID_COLUMN)) result.put(PENDINGID_COLUMN, 0);
- // This is a binary blob of a dictionary
- if (null == result.get(TYPE_COLUMN)) result.put(TYPE_COLUMN, TYPE_BULK);
- // This word list is unknown, but it's present, else we wouldn't be here, so INSTALLED
- if (null == result.get(STATUS_COLUMN)) result.put(STATUS_COLUMN, STATUS_INSTALLED);
- // No description unless specified, because we can't guess it
- if (null == result.get(DESCRIPTION_COLUMN)) result.put(DESCRIPTION_COLUMN, "");
- // File name - this is an asset, so it works as an already deleted file.
- // hence, we need to supply a non-existent file name. Anything will
- // do as long as it returns false when tested with File#exist(), and
- // the empty string does not, so it's set to "_".
- if (null == result.get(LOCAL_FILENAME_COLUMN)) result.put(LOCAL_FILENAME_COLUMN, "_");
- // No remote file name : this can't be downloaded. Unless specified.
- if (null == result.get(REMOTE_FILENAME_COLUMN)) result.put(REMOTE_FILENAME_COLUMN, "");
- // 0 for the update date : 1970/1/1. Unless specified.
- if (null == result.get(DATE_COLUMN)) result.put(DATE_COLUMN, 0);
- // Raw checksum unknown unless specified
- if (null == result.get(RAW_CHECKSUM_COLUMN)) result.put(RAW_CHECKSUM_COLUMN, "");
- // Retry column 0 unless specified
- if (null == result.get(RETRY_COUNT_COLUMN)) result.put(RETRY_COUNT_COLUMN,
- DICTIONARY_RETRY_THRESHOLD);
- // Checksum unknown unless specified
- if (null == result.get(CHECKSUM_COLUMN)) result.put(CHECKSUM_COLUMN, "");
- // No filesize unless specified
- if (null == result.get(FILESIZE_COLUMN)) result.put(FILESIZE_COLUMN, 0);
- // Smallest possible version unless specified
- if (null == result.get(VERSION_COLUMN)) result.put(VERSION_COLUMN, 1);
- // Assume current format unless specified
- if (null == result.get(FORMATVERSION_COLUMN))
- result.put(FORMATVERSION_COLUMN, UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION);
- // No flags unless specified
- if (null == result.get(FLAGS_COLUMN)) result.put(FLAGS_COLUMN, 0);
- return result;
- }
-
- /**
- * Reads a column in a Cursor as a String and stores it in a ContentValues object.
- * @param result the ContentValues object to store the result in.
- * @param cursor the Cursor to read the column from.
- * @param columnId the column ID to read.
- */
- private static void putStringResult(ContentValues result, Cursor cursor, String columnId) {
- result.put(columnId, cursor.getString(cursor.getColumnIndex(columnId)));
- }
-
- /**
- * Reads a column in a Cursor as an int and stores it in a ContentValues object.
- * @param result the ContentValues object to store the result in.
- * @param cursor the Cursor to read the column from.
- * @param columnId the column ID to read.
- */
- private static void putIntResult(ContentValues result, Cursor cursor, String columnId) {
- result.put(columnId, cursor.getInt(cursor.getColumnIndex(columnId)));
- }
-
- private static ContentValues getFirstLineAsContentValues(final Cursor cursor) {
- final ContentValues result;
- if (cursor.moveToFirst()) {
- result = new ContentValues(COLUMN_COUNT);
- putIntResult(result, cursor, PENDINGID_COLUMN);
- putIntResult(result, cursor, TYPE_COLUMN);
- putIntResult(result, cursor, STATUS_COLUMN);
- putStringResult(result, cursor, WORDLISTID_COLUMN);
- putStringResult(result, cursor, LOCALE_COLUMN);
- putStringResult(result, cursor, DESCRIPTION_COLUMN);
- putStringResult(result, cursor, LOCAL_FILENAME_COLUMN);
- putStringResult(result, cursor, REMOTE_FILENAME_COLUMN);
- putIntResult(result, cursor, DATE_COLUMN);
- putStringResult(result, cursor, RAW_CHECKSUM_COLUMN);
- putStringResult(result, cursor, CHECKSUM_COLUMN);
- putIntResult(result, cursor, RETRY_COUNT_COLUMN);
- putIntResult(result, cursor, FILESIZE_COLUMN);
- putIntResult(result, cursor, VERSION_COLUMN);
- putIntResult(result, cursor, FORMATVERSION_COLUMN);
- putIntResult(result, cursor, FLAGS_COLUMN);
- if (cursor.moveToNext()) {
- // TODO: print the second level of the stack to the log so that we know
- // in which code path the error happened
- Log.e(TAG, "Several SQL results when we expected only one!");
- }
- } else {
- result = null;
- }
- return result;
- }
-
- /**
- * Gets the info about as specific download, indexed by its DownloadManager ID.
- * @param db the database to get the information from.
- * @param id the DownloadManager id.
- * @return metadata about this download. This returns all columns in the database.
- */
- public static ContentValues getContentValuesByPendingId(final SQLiteDatabase db,
- final long id) {
- final Cursor cursor = db.query(METADATA_TABLE_NAME,
- METADATA_TABLE_COLUMNS,
- PENDINGID_COLUMN + "= ?",
- new String[] { Long.toString(id) },
- null, null, null);
- if (null == cursor) {
- return null;
- }
- try {
- // There should never be more than one result. If because of some bug there are,
- // returning only one result is the right thing to do, because we couldn't handle
- // several anyway and we should still handle one.
- return getFirstLineAsContentValues(cursor);
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Gets the info about an installed OR deleting word list with a specified id.
- *
- * Basically, this is the word list that we want to return to Android Keyboard when
- * it asks for a specific id.
- *
- * @param db the database to get the information from.
- * @param id the word list ID.
- * @return the metadata about this word list.
- */
- public static ContentValues getInstalledOrDeletingWordListContentValuesByWordListId(
- final SQLiteDatabase db, final String id) {
- final Cursor cursor = db.query(METADATA_TABLE_NAME,
- METADATA_TABLE_COLUMNS,
- WORDLISTID_COLUMN + "=? AND (" + STATUS_COLUMN + "=? OR " + STATUS_COLUMN + "=?)",
- new String[] { id, Integer.toString(STATUS_INSTALLED),
- Integer.toString(STATUS_DELETING) },
- null, null, null);
- if (null == cursor) {
- return null;
- }
- try {
- // There should only be one result, but if there are several, we can't tell which
- // is the best, so we just return the first one.
- return getFirstLineAsContentValues(cursor);
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Given a specific download ID, return records for all pending downloads across all clients.
- *
- * If several clients use the same metadata URL, we know to only download it once, and
- * dispatch the update process across all relevant clients when the download ends. This means
- * several clients may share a single download ID if they share a metadata URI.
- * The dispatching is done in
- * {@link UpdateHandler#downloadFinished(Context, android.content.Intent)}, which
- * finds out about the list of relevant clients by calling this method.
- *
- * @param context a context instance to open the databases
- * @param downloadId the download ID to query about
- * @return the list of records. Never null, but may be empty.
- */
- public static ArrayList<DownloadRecord> getDownloadRecordsForDownloadId(final Context context,
- final long downloadId) {
- final SQLiteDatabase defaultDb = getDb(context, "");
- final ArrayList<DownloadRecord> results = new ArrayList<>();
- final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, CLIENT_TABLE_COLUMNS,
- null, null, null, null, null);
- try {
- if (!cursor.moveToFirst()) return results;
- final int clientIdIndex = cursor.getColumnIndex(CLIENT_CLIENT_ID_COLUMN);
- final int pendingIdColumn = cursor.getColumnIndex(CLIENT_PENDINGID_COLUMN);
- do {
- final long pendingId = cursor.getInt(pendingIdColumn);
- final String clientId = cursor.getString(clientIdIndex);
- if (pendingId == downloadId) {
- results.add(new DownloadRecord(clientId, null));
- }
- final ContentValues valuesForThisClient =
- getContentValuesByPendingId(getDb(context, clientId), downloadId);
- if (null != valuesForThisClient) {
- results.add(new DownloadRecord(clientId, valuesForThisClient));
- }
- } while (cursor.moveToNext());
- } finally {
- cursor.close();
- }
- return results;
- }
-
- /**
- * Gets the info about a specific word list.
- *
- * @param db the database to get the information from.
- * @param id the word list ID.
- * @param version the word list version.
- * @return the metadata about this word list.
- */
- @Nullable
- public static ContentValues getContentValuesByWordListId(final SQLiteDatabase db,
- final String id, final int version) {
- final Cursor cursor = db.query(METADATA_TABLE_NAME,
- METADATA_TABLE_COLUMNS,
- WORDLISTID_COLUMN + "= ? AND " + VERSION_COLUMN + "= ? AND "
- + FORMATVERSION_COLUMN + "<= ?",
- new String[]
- { id,
- Integer.toString(version),
- Integer.toString(UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION)
- },
- null /* groupBy */,
- null /* having */,
- FORMATVERSION_COLUMN + " DESC"/* orderBy */);
- if (null == cursor) {
- return null;
- }
- try {
- // This is a lookup by primary key, so there can't be more than one result.
- return getFirstLineAsContentValues(cursor);
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Gets the info about the latest word list with an id.
- *
- * @param db the database to get the information from.
- * @param id the word list ID.
- * @return the metadata about the word list with this id and the latest version number.
- */
- public static ContentValues getContentValuesOfLatestAvailableWordlistById(
- final SQLiteDatabase db, final String id) {
- final Cursor cursor = db.query(METADATA_TABLE_NAME,
- METADATA_TABLE_COLUMNS,
- WORDLISTID_COLUMN + "= ?",
- new String[] { id }, null, null, VERSION_COLUMN + " DESC", "1");
- if (null == cursor) {
- return null;
- }
- try {
- // Return the first result from the list of results.
- return getFirstLineAsContentValues(cursor);
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Gets the current metadata about INSTALLED, AVAILABLE or DELETING dictionaries.
- *
- * This odd method is tailored to the needs of
- * DictionaryProvider#getDictionaryWordListsForContentUri, which needs the word list if
- * it is:
- * - INSTALLED: this should be returned to LatinIME if the file is still inside the dictionary
- * pack, so that it can be copied. If the file is not there, it's been copied already and should
- * not be returned, so getDictionaryWordListsForContentUri takes care of this.
- * - DELETING: this should be returned to LatinIME so that it can actually delete the file.
- * - AVAILABLE: this should not be returned, but should be checked for auto-installation.
- *
- * @param context the context for getting the database.
- * @param clientId the client id for retrieving the database. null for default (deprecated)
- * @return a cursor with metadata about usable dictionaries.
- */
- public static Cursor queryInstalledOrDeletingOrAvailableDictionaryMetadata(
- final Context context, final String clientId) {
- // If clientId is null, we get the defaut DB (see #getInstance() for more about this)
- final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME,
- METADATA_TABLE_COLUMNS,
- STATUS_COLUMN + " = ? OR " + STATUS_COLUMN + " = ? OR " + STATUS_COLUMN + " = ?",
- new String[] { Integer.toString(STATUS_INSTALLED),
- Integer.toString(STATUS_DELETING),
- Integer.toString(STATUS_AVAILABLE) },
- null, null, LOCALE_COLUMN);
- return results;
- }
-
- /**
- * Gets the current metadata about all dictionaries.
- *
- * This will retrieve the metadata about all dictionaries, including
- * older files, or files not yet downloaded.
- *
- * @param context the context for getting the database.
- * @param clientId the client id for retrieving the database. null for default (deprecated)
- * @return a cursor with metadata about usable dictionaries.
- */
- public static Cursor queryCurrentMetadata(final Context context, final String clientId) {
- // If clientId is null, we get the defaut DB (see #getInstance() for more about this)
- final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME,
- METADATA_TABLE_COLUMNS, null, null, null, null, LOCALE_COLUMN);
- return results;
- }
-
- /**
- * Gets the list of all dictionaries known to the dictionary provider, with only public columns.
- *
- * This will retrieve information about all known dictionaries, and their status. As such,
- * it will also return information about dictionaries on the server that have not been
- * downloaded yet, but may be requested.
- * This only returns public columns. It does not populate internal columns in the returned
- * cursor.
- * The value returned by this method is intended to be good to be returned directly for a
- * request of the list of dictionaries by a client.
- *
- * @param context the context to read the database from.
- * @param clientId the client id for retrieving the database. null for default (deprecated)
- * @return a cursor that lists all available dictionaries and their metadata.
- */
- public static Cursor queryDictionaries(final Context context, final String clientId) {
- // If clientId is null, we get the defaut DB (see #getInstance() for more about this)
- final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME,
- DICTIONARIES_LIST_PUBLIC_COLUMNS,
- // Filter out empty locales so as not to return auxiliary data, like a
- // data line for downloading metadata:
- MetadataDbHelper.LOCALE_COLUMN + " != ?", new String[] {""},
- // TODO: Reinstate the following code for bulk, then implement partial updates
- /* MetadataDbHelper.TYPE_COLUMN + " = ?",
- new String[] { Integer.toString(MetadataDbHelper.TYPE_BULK) }, */
- null, null, LOCALE_COLUMN);
- return results;
- }
-
- /**
- * Deletes all data associated with a client.
- *
- * @param context the context for opening the database
- * @param clientId the ID of the client to delete.
- * @return true if the client was successfully deleted, false otherwise.
- */
- public static boolean deleteClient(final Context context, final String clientId) {
- // Remove all metadata associated with this client
- final SQLiteDatabase db = getDb(context, clientId);
- db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME);
- db.execSQL(METADATA_TABLE_CREATE);
- // Remove this client's entry in the clients table
- final SQLiteDatabase defaultDb = getDb(context, "");
- if (0 == defaultDb.delete(CLIENT_TABLE_NAME,
- CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId })) {
- return false;
- }
- return true;
- }
-
- /**
- * Updates information relative to a specific client.
- *
- * Updatable information includes the metadata URI and the additional ID column. It may be
- * expanded in the future.
- * The passed values must include a client ID in the key CLIENT_CLIENT_ID_COLUMN, and it must
- * be equal to the string passed as an argument for clientId. It may not be empty.
- * The passed values must also include a non-null metadata URI in the
- * CLIENT_METADATA_URI_COLUMN column, as well as a non-null additional ID in the
- * CLIENT_METADATA_ADDITIONAL_ID_COLUMN. Both these strings may be empty.
- * If any of the above is not complied with, this function returns without updating data.
- *
- * @param context the context, to open the database
- * @param clientId the ID of the client to update
- * @param values the values to update. Must conform to the protocol (see above)
- */
- public static void updateClientInfo(final Context context, final String clientId,
- final ContentValues values) {
- // Validity check the content values
- final String valuesClientId = values.getAsString(CLIENT_CLIENT_ID_COLUMN);
- final String valuesMetadataUri = values.getAsString(CLIENT_METADATA_URI_COLUMN);
- final String valuesMetadataAdditionalId =
- values.getAsString(CLIENT_METADATA_ADDITIONAL_ID_COLUMN);
- // Empty string is a valid client ID, but external apps may not configure it, so disallow
- // both null and empty string.
- // Empty string is a valid metadata URI if the client does not want updates, so allow
- // empty string but disallow null.
- // Empty string is a valid additional ID so allow empty string but disallow null.
- if (TextUtils.isEmpty(valuesClientId) || null == valuesMetadataUri
- || null == valuesMetadataAdditionalId) {
- // We need all these columns to be filled in
- DebugLogUtils.l("Missing parameter for updateClientInfo");
- return;
- }
- if (!clientId.equals(valuesClientId)) {
- // Mismatch! The client violates the protocol.
- DebugLogUtils.l("Received an updateClientInfo request for ", clientId,
- " but the values " + "contain a different ID : ", valuesClientId);
- return;
- }
- // Default value for a pending ID is NOT_AN_ID
- values.put(CLIENT_PENDINGID_COLUMN, UpdateHandler.NOT_AN_ID);
- final SQLiteDatabase defaultDb = getDb(context, "");
- if (-1 == defaultDb.insert(CLIENT_TABLE_NAME, null, values)) {
- defaultDb.update(CLIENT_TABLE_NAME, values,
- CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId });
- }
- }
-
- /**
- * Retrieves the list of existing client IDs.
- * @param context the context to open the database
- * @return a cursor containing only one column, and one client ID per line.
- */
- public static Cursor queryClientIds(final Context context) {
- return getDb(context, null).query(CLIENT_TABLE_NAME,
- new String[] { CLIENT_CLIENT_ID_COLUMN }, null, null, null, null, null);
- }
-
- /**
- * Register a download ID for a specific metadata URI.
- *
- * This method should be called when a download for a metadata URI is starting. It will
- * search for all clients using this metadata URI and will register for each of them
- * the download ID into the database for later retrieval by
- * {@link #getDownloadRecordsForDownloadId(Context, long)}.
- *
- * @param context a context for opening databases
- * @param uri the metadata URI
- * @param downloadId the download ID
- */
- public static void registerMetadataDownloadId(final Context context, final String uri,
- final long downloadId) {
- final ContentValues values = new ContentValues();
- values.put(CLIENT_PENDINGID_COLUMN, downloadId);
- values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis());
- final SQLiteDatabase defaultDb = getDb(context, "");
- final Cursor cursor = MetadataDbHelper.queryClientIds(context);
- if (null == cursor) return;
- try {
- if (!cursor.moveToFirst()) return;
- do {
- final String clientId = cursor.getString(0);
- final String metadataUri =
- MetadataDbHelper.getMetadataUriAsString(context, clientId);
- if (metadataUri.equals(uri)) {
- defaultDb.update(CLIENT_TABLE_NAME, values,
- CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId });
- }
- } while (cursor.moveToNext());
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Marks a downloading entry as having successfully downloaded and being installed.
- *
- * The metadata database contains information about ongoing processes, typically ongoing
- * downloads. This marks such an entry as having finished and having installed successfully,
- * so it becomes INSTALLED.
- *
- * @param db the metadata database.
- * @param r content values about the entry to mark as processed.
- */
- public static void markEntryAsFinishedDownloadingAndInstalled(final SQLiteDatabase db,
- final ContentValues r) {
- switch (r.getAsInteger(TYPE_COLUMN)) {
- case TYPE_BULK:
- DebugLogUtils.l("Ended processing a wordlist");
- // Updating a bulk word list is a three-step operation:
- // - Add the new entry to the table
- // - Remove the old entry from the table
- // - Erase the old file
- // We start by gathering the names of the files we should delete.
- final List<String> filenames = new LinkedList<>();
- final Cursor c = db.query(METADATA_TABLE_NAME,
- new String[] { LOCAL_FILENAME_COLUMN },
- LOCALE_COLUMN + " = ? AND " +
- WORDLISTID_COLUMN + " = ? AND " + STATUS_COLUMN + " = ?",
- new String[] { r.getAsString(LOCALE_COLUMN),
- r.getAsString(WORDLISTID_COLUMN),
- Integer.toString(STATUS_INSTALLED) },
- null, null, null);
- try {
- if (c.moveToFirst()) {
- // There should never be more than one file, but if there are, it's a bug
- // and we should remove them all. I think it might happen if the power of
- // the phone is suddenly cut during an update.
- final int filenameIndex = c.getColumnIndex(LOCAL_FILENAME_COLUMN);
- do {
- DebugLogUtils.l("Setting for removal", c.getString(filenameIndex));
- filenames.add(c.getString(filenameIndex));
- } while (c.moveToNext());
- }
- } finally {
- c.close();
- }
- r.put(STATUS_COLUMN, STATUS_INSTALLED);
- db.beginTransactionNonExclusive();
- // Delete all old entries. There should never be any stalled entries, but if
- // there are, this deletes them.
- db.delete(METADATA_TABLE_NAME,
- WORDLISTID_COLUMN + " = ?",
- new String[] { r.getAsString(WORDLISTID_COLUMN) });
- db.insert(METADATA_TABLE_NAME, null, r);
- db.setTransactionSuccessful();
- db.endTransaction();
- for (String filename : filenames) {
- try {
- final File f = new File(filename);
- f.delete();
- } catch (SecurityException e) {
- // No permissions to delete. Um. Can't do anything.
- } // I don't think anything else can be thrown
- }
- break;
- default:
- // Unknown type: do nothing.
- break;
- }
- }
-
- /**
- * Removes a downloading entry from the database.
- *
- * This is invoked when a download fails. Either we tried to download, but
- * we received a permanent failure and we should remove it, or we got manually
- * cancelled and we should leave it at that.
- *
- * @param db the metadata database.
- * @param id the DownloadManager id of the file.
- */
- public static void deleteDownloadingEntry(final SQLiteDatabase db, final long id) {
- db.delete(METADATA_TABLE_NAME, PENDINGID_COLUMN + " = ? AND " + STATUS_COLUMN + " = ?",
- new String[] { Long.toString(id), Integer.toString(STATUS_DOWNLOADING) });
- }
-
- /**
- * Forcefully removes an entry from the database.
- *
- * This is invoked when a file is broken. The file has been downloaded, but Android
- * Keyboard is telling us it could not open it.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- */
- public static void deleteEntry(final SQLiteDatabase db, final String id, final int version) {
- db.delete(METADATA_TABLE_NAME, WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?",
- new String[] { id, Integer.toString(version) });
- }
-
- /**
- * Internal method that sets the current status of an entry of the database.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- * @param status the status to set the word list to.
- * @param downloadId an optional download id to write, or NOT_A_DOWNLOAD_ID
- */
- private static void markEntryAs(final SQLiteDatabase db, final String id,
- final int version, final int status, final long downloadId) {
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db, id, version);
- values.put(STATUS_COLUMN, status);
- if (NOT_A_DOWNLOAD_ID != downloadId) {
- values.put(MetadataDbHelper.PENDINGID_COLUMN, downloadId);
- }
- db.update(METADATA_TABLE_NAME, values,
- WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?",
- new String[] { id, Integer.toString(version) });
- }
-
- /**
- * Writes the status column for the wordlist with this id as enabled. Typically this
- * means the word list is currently disabled and we want to set its status to INSTALLED.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- */
- public static void markEntryAsEnabled(final SQLiteDatabase db, final String id,
- final int version) {
- markEntryAs(db, id, version, STATUS_INSTALLED, NOT_A_DOWNLOAD_ID);
- }
-
- /**
- * Writes the status column for the wordlist with this id as disabled. Typically this
- * means the word list is currently installed and we want to set its status to DISABLED.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- */
- public static void markEntryAsDisabled(final SQLiteDatabase db, final String id,
- final int version) {
- markEntryAs(db, id, version, STATUS_DISABLED, NOT_A_DOWNLOAD_ID);
- }
-
- /**
- * Writes the status column for the wordlist with this id as available. This happens for
- * example when a word list has been deleted but can be downloaded again.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- */
- public static void markEntryAsAvailable(final SQLiteDatabase db, final String id,
- final int version) {
- markEntryAs(db, id, version, STATUS_AVAILABLE, NOT_A_DOWNLOAD_ID);
- }
-
- /**
- * Writes the designated word list as downloadable, alongside with its download id.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- * @param downloadId the download id.
- */
- public static void markEntryAsDownloading(final SQLiteDatabase db, final String id,
- final int version, final long downloadId) {
- markEntryAs(db, id, version, STATUS_DOWNLOADING, downloadId);
- }
-
- /**
- * Writes the designated word list as deleting.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- */
- public static void markEntryAsDeleting(final SQLiteDatabase db, final String id,
- final int version) {
- markEntryAs(db, id, version, STATUS_DELETING, NOT_A_DOWNLOAD_ID);
- }
-
- /**
- * Checks retry counts and marks the word list as retrying if retry is possible.
- *
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- * @return {@code true} if the retry is possible.
- */
- public static boolean maybeMarkEntryAsRetrying(final SQLiteDatabase db, final String id,
- final int version) {
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db, id, version);
- int retryCount = values.getAsInteger(MetadataDbHelper.RETRY_COUNT_COLUMN);
- if (retryCount > 1) {
- values.put(STATUS_COLUMN, STATUS_RETRYING);
- values.put(RETRY_COUNT_COLUMN, retryCount - 1);
- db.update(METADATA_TABLE_NAME, values,
- WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?",
- new String[] { id, Integer.toString(version) });
- return true;
- }
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
deleted file mode 100644
index e5d632fbe..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Collections;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class to easy up manipulation of dictionary pack metadata.
- */
-public class MetadataHandler {
-
- public static final String TAG = MetadataHandler.class.getSimpleName();
-
- // The canonical file name for metadata. This is not the name of a real file on the
- // device, but a symbolic name used in the database and in metadata handling. It is never
- // tested against, only used for human-readability as the file name for the metadata.
- public static final String METADATA_FILENAME = "metadata.json";
-
- /**
- * Reads the data from the cursor and store it in metadata objects.
- * @param results the cursor to read data from.
- * @return the constructed list of wordlist metadata.
- */
- private static List<WordListMetadata> makeMetadataObject(final Cursor results) {
- final ArrayList<WordListMetadata> buildingMetadata = new ArrayList<>();
- if (null != results && results.moveToFirst()) {
- final int localeColumn = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
- final int typeColumn = results.getColumnIndex(MetadataDbHelper.TYPE_COLUMN);
- final int descriptionColumn =
- results.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN);
- final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
- final int updateIndex = results.getColumnIndex(MetadataDbHelper.DATE_COLUMN);
- final int fileSizeIndex = results.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
- final int rawChecksumIndex =
- results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN);
- final int checksumIndex = results.getColumnIndex(MetadataDbHelper.CHECKSUM_COLUMN);
- final int retryCountIndex = results.getColumnIndex(MetadataDbHelper.RETRY_COUNT_COLUMN);
- final int localFilenameIndex =
- results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
- final int remoteFilenameIndex =
- results.getColumnIndex(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
- final int versionIndex = results.getColumnIndex(MetadataDbHelper.VERSION_COLUMN);
- final int formatVersionIndex =
- results.getColumnIndex(MetadataDbHelper.FORMATVERSION_COLUMN);
- do {
- buildingMetadata.add(new WordListMetadata(results.getString(idIndex),
- results.getInt(typeColumn),
- results.getString(descriptionColumn),
- results.getLong(updateIndex),
- results.getLong(fileSizeIndex),
- results.getString(rawChecksumIndex),
- results.getString(checksumIndex),
- results.getInt(retryCountIndex),
- results.getString(localFilenameIndex),
- results.getString(remoteFilenameIndex),
- results.getInt(versionIndex),
- results.getInt(formatVersionIndex),
- 0, results.getString(localeColumn)));
- } while (results.moveToNext());
- }
- return Collections.unmodifiableList(buildingMetadata);
- }
-
- /**
- * Gets the whole metadata, for installed and not installed dictionaries.
- * @param context The context to open files over.
- * @param clientId the client id for retrieving the database. null for default (deprecated)
- * @return The current metadata.
- */
- public static List<WordListMetadata> getCurrentMetadata(final Context context,
- final String clientId) {
- // If clientId is null, we get a cursor on the default database (see
- // MetadataDbHelper#getInstance() for more on this)
- final Cursor results = MetadataDbHelper.queryCurrentMetadata(context, clientId);
- // If null, we should return makeMetadataObject(null), so we go through.
- try {
- return makeMetadataObject(results);
- } finally {
- if (null != results) {
- results.close();
- }
- }
- }
-
- /**
- * Gets the metadata, for a specific dictionary.
- *
- * @param context The context to open files over.
- * @param clientId the client id for retrieving the database. null for default (deprecated).
- * @param wordListId the word list ID.
- * @param version the word list version.
- * @return the current metaData
- */
- public static WordListMetadata getCurrentMetadataForWordList(final Context context,
- final String clientId, final String wordListId, final int version) {
- final ContentValues contentValues = MetadataDbHelper.getContentValuesByWordListId(
- MetadataDbHelper.getDb(context, clientId), wordListId, version);
- if (contentValues == null) {
- // TODO: Figure out why this would happen.
- // Check if this happens when the metadata gets updated in the background.
- Log.e(TAG, String.format( "Unable to find the current metadata for wordlist "
- + "(clientId=%s, wordListId=%s, version=%d) on the database",
- clientId, wordListId, version));
- return null;
- }
- return WordListMetadata.createFromContentValues(contentValues);
- }
-
- /**
- * Read metadata from a stream.
- * @param input The stream to read from.
- * @return The read metadata.
- * @throws IOException if the input stream cannot be read
- * @throws BadFormatException if the stream is not in a known format
- */
- public static List<WordListMetadata> readMetadata(final InputStreamReader input)
- throws IOException, BadFormatException {
- return MetadataParser.parseMetadata(input);
- }
-
- /**
- * Finds a single WordListMetadata inside a whole metadata chunk.
- *
- * Searches through the whole passed metadata for the first WordListMetadata associated
- * with the passed ID. If several metadata chunks with the same id are found, it will
- * always return the one with the bigger FormatVersion that is less or equal than the
- * maximum supported format version (as listed in UpdateHandler).
- * This will NEVER return the metadata with a FormatVersion bigger than what is supported,
- * even if it is the only word list with this ID.
- *
- * @param metadata the metadata to search into.
- * @param id the word list ID of the metadata to find.
- * @return the associated metadata, or null if not found.
- */
- public static WordListMetadata findWordListById(final List<WordListMetadata> metadata,
- final String id) {
- WordListMetadata bestWordList = null;
- int bestFormatVersion = Integer.MIN_VALUE; // To be sure we can't be inadvertently smaller
- for (WordListMetadata wordList : metadata) {
- if (id.equals(wordList.mId)
- && wordList.mFormatVersion <= UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION
- && wordList.mFormatVersion > bestFormatVersion) {
- bestWordList = wordList;
- bestFormatVersion = wordList.mFormatVersion;
- }
- }
- // If we didn't find any match we'll return null.
- return bestWordList;
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java b/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java
deleted file mode 100644
index 2b67ae9ff..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.text.TextUtils;
-import android.util.JsonReader;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.TreeMap;
-
-/**
- * Helper class containing functions to parse the dictionary metadata.
- */
-public class MetadataParser {
-
- // Name of the fields in the JSON-formatted file.
- private static final String ID_FIELD_NAME = MetadataDbHelper.WORDLISTID_COLUMN;
- private static final String LOCALE_FIELD_NAME = "locale";
- private static final String DESCRIPTION_FIELD_NAME = MetadataDbHelper.DESCRIPTION_COLUMN;
- private static final String UPDATE_FIELD_NAME = "update";
- private static final String FILESIZE_FIELD_NAME = MetadataDbHelper.FILESIZE_COLUMN;
- private static final String RAW_CHECKSUM_FIELD_NAME = MetadataDbHelper.RAW_CHECKSUM_COLUMN;
- private static final String CHECKSUM_FIELD_NAME = MetadataDbHelper.CHECKSUM_COLUMN;
- private static final String REMOTE_FILENAME_FIELD_NAME =
- MetadataDbHelper.REMOTE_FILENAME_COLUMN;
- private static final String VERSION_FIELD_NAME = MetadataDbHelper.VERSION_COLUMN;
- private static final String FORMATVERSION_FIELD_NAME = MetadataDbHelper.FORMATVERSION_COLUMN;
-
- /**
- * Parse one JSON-formatted word list metadata.
- * @param reader the reader containing the data.
- * @return a WordListMetadata object from the parsed data.
- * @throws IOException if the underlying reader throws IOException during reading.
- */
- private static WordListMetadata parseOneWordList(final JsonReader reader)
- throws IOException, BadFormatException {
- final TreeMap<String, String> arguments = new TreeMap<>();
- reader.beginObject();
- while (reader.hasNext()) {
- final String name = reader.nextName();
- if (!TextUtils.isEmpty(name)) {
- arguments.put(name, reader.nextString());
- }
- }
- reader.endObject();
- if (TextUtils.isEmpty(arguments.get(ID_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(LOCALE_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(DESCRIPTION_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(UPDATE_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(FILESIZE_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(CHECKSUM_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(REMOTE_FILENAME_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(VERSION_FIELD_NAME))
- || TextUtils.isEmpty(arguments.get(FORMATVERSION_FIELD_NAME))) {
- throw new BadFormatException(arguments.toString());
- }
- // TODO: need to find out whether it's bulk or update
- // The null argument is the local file name, which is not known at this time and will
- // be decided later.
- return new WordListMetadata(
- arguments.get(ID_FIELD_NAME),
- MetadataDbHelper.TYPE_BULK,
- arguments.get(DESCRIPTION_FIELD_NAME),
- Long.parseLong(arguments.get(UPDATE_FIELD_NAME)),
- Long.parseLong(arguments.get(FILESIZE_FIELD_NAME)),
- arguments.get(RAW_CHECKSUM_FIELD_NAME),
- arguments.get(CHECKSUM_FIELD_NAME),
- MetadataDbHelper.DICTIONARY_RETRY_THRESHOLD /* retryCount */,
- null,
- arguments.get(REMOTE_FILENAME_FIELD_NAME),
- Integer.parseInt(arguments.get(VERSION_FIELD_NAME)),
- Integer.parseInt(arguments.get(FORMATVERSION_FIELD_NAME)),
- 0, arguments.get(LOCALE_FIELD_NAME));
- }
-
- /**
- * Parses metadata in the JSON format.
- * @param input a stream reader expected to contain JSON formatted metadata.
- * @return dictionary metadata, as an array of WordListMetadata objects.
- * @throws IOException if the underlying reader throws IOException during reading.
- * @throws BadFormatException if the data was not in the expected format.
- */
- public static List<WordListMetadata> parseMetadata(final InputStreamReader input)
- throws IOException, BadFormatException {
- JsonReader reader = new JsonReader(input);
- final ArrayList<WordListMetadata> readInfo = new ArrayList<>();
- reader.beginArray();
- while (reader.hasNext()) {
- final WordListMetadata thisMetadata = parseOneWordList(reader);
- if (!TextUtils.isEmpty(thisMetadata.mLocale))
- readInfo.add(thisMetadata);
- }
- return Collections.unmodifiableList(readInfo);
- }
-
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java b/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java
deleted file mode 100644
index 512d426aa..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.Context;
-
-/**
- * Helper to get the metadata URI from its base URI.
- */
-@SuppressWarnings("unused")
-public class MetadataUriGetter {
- public static String getUri(final Context context, final String baseUri) {
- return baseUri;
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java
deleted file mode 100644
index bb64721d5..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-/**
- * Class to keep long-term log. This is inactive in production, and is only for debug purposes.
- */
-public class PrivateLog {
-
- public static final boolean DEBUG = DictionaryProvider.DEBUG;
-
- private static final String LOG_DATABASE_NAME = "log";
- private static final String LOG_TABLE_NAME = "log";
- private static final int LOG_DATABASE_VERSION = 1;
-
- private static final String COLUMN_DATE = "date";
- private static final String COLUMN_EVENT = "event";
-
- private static final String LOG_TABLE_CREATE = "CREATE TABLE " + LOG_TABLE_NAME + " ("
- + COLUMN_DATE + " TEXT,"
- + COLUMN_EVENT + " TEXT);";
-
- static final SimpleDateFormat sDateFormat = new SimpleDateFormat(
- "yyyy/MM/dd HH:mm:ss", Locale.ROOT);
-
- private static PrivateLog sInstance = new PrivateLog();
- private static DebugHelper sDebugHelper = null;
-
- private PrivateLog() {
- }
-
- public static synchronized PrivateLog getInstance(final Context context) {
- if (!DEBUG) return sInstance;
- synchronized(PrivateLog.class) {
- if (sDebugHelper == null) {
- sDebugHelper = new DebugHelper(context);
- }
- return sInstance;
- }
- }
-
- static class DebugHelper extends SQLiteOpenHelper {
-
- DebugHelper(final Context context) {
- super(context, LOG_DATABASE_NAME, null, LOG_DATABASE_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- if (!DEBUG) return;
- db.execSQL(LOG_TABLE_CREATE);
- insert(db, "Created table");
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (!DEBUG) return;
- // Remove all data.
- db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE_NAME);
- onCreate(db);
- insert(db, "Upgrade finished");
- }
-
- static void insert(SQLiteDatabase db, String event) {
- if (!DEBUG) return;
- final ContentValues c = new ContentValues(2);
- c.put(COLUMN_DATE, sDateFormat.format(new Date(System.currentTimeMillis())));
- c.put(COLUMN_EVENT, event);
- db.insert(LOG_TABLE_NAME, null, c);
- }
-
- }
-
- public static void log(String event) {
- if (!DEBUG) return;
- final SQLiteDatabase l = sDebugHelper.getWritableDatabase();
- DebugHelper.insert(l, event);
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/ProblemReporter.java b/java/src/com/android/inputmethod/dictionarypack/ProblemReporter.java
deleted file mode 100644
index 632819aa3..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/ProblemReporter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-/**
- * A simple interface to report problems.
- */
-public interface ProblemReporter {
- public void report(Exception e);
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
deleted file mode 100644
index bdea3e919..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ /dev/null
@@ -1,1083 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.app.DownloadManager;
-import android.app.DownloadManager.Query;
-import android.app.DownloadManager.Request;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.ConnectivityManager;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.utils.ApplicationUtils;
-import com.android.inputmethod.latin.utils.DebugLogUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.annotation.Nullable;
-
-/**
- * Handler for the update process.
- *
- * This class is in charge of coordinating the update process for the various dictionaries
- * stored in the dictionary pack.
- */
-public final class UpdateHandler {
- static final String TAG = "DictionaryProvider:" + UpdateHandler.class.getSimpleName();
- private static final boolean DEBUG = DictionaryProvider.DEBUG;
-
- // Used to prevent trying to read the id of the downloaded file before it is written
- static final Object sSharedIdProtector = new Object();
-
- // Value used to mean this is not a real DownloadManager downloaded file id
- // DownloadManager uses as an ID numbers returned out of an AUTOINCREMENT column
- // in SQLite, so it should never return anything < 0.
- public static final int NOT_AN_ID = -1;
- public static final int MAXIMUM_SUPPORTED_FORMAT_VERSION =
- FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION;
-
- // Arbitrary. Probably good if it's a power of 2, and a couple thousand bytes long.
- private static final int FILE_COPY_BUFFER_SIZE = 8192;
-
- // Table fixed values for metadata / downloads
- final static String METADATA_NAME = "metadata";
- final static int METADATA_TYPE = 0;
- final static int WORDLIST_TYPE = 1;
-
- // Suffix for generated dictionary files
- private static final String DICT_FILE_SUFFIX = ".dict";
- // Name of the category for the main dictionary
- public static final String MAIN_DICTIONARY_CATEGORY = "main";
-
- public static final String TEMP_DICT_FILE_SUB = "___";
-
- // The id for the "dictionary available" notification.
- static final int DICT_AVAILABLE_NOTIFICATION_ID = 1;
-
- /**
- * An interface for UIs or services that want to know when something happened.
- *
- * This is chiefly used by the dictionary manager UI.
- */
- public interface UpdateEventListener {
- void downloadedMetadata(boolean succeeded);
- void wordListDownloadFinished(String wordListId, boolean succeeded);
- void updateCycleCompleted();
- }
-
- /**
- * The list of currently registered listeners.
- */
- private static List<UpdateEventListener> sUpdateEventListeners
- = Collections.synchronizedList(new LinkedList<UpdateEventListener>());
-
- /**
- * Register a new listener to be notified of updates.
- *
- * Don't forget to call unregisterUpdateEventListener when done with it, or
- * it will leak the register.
- */
- public static void registerUpdateEventListener(final UpdateEventListener listener) {
- sUpdateEventListeners.add(listener);
- }
-
- /**
- * Unregister a previously registered listener.
- */
- public static void unregisterUpdateEventListener(final UpdateEventListener listener) {
- sUpdateEventListeners.remove(listener);
- }
-
- private static final String DOWNLOAD_OVER_METERED_SETTING_PREFS_KEY = "downloadOverMetered";
-
- /**
- * Write the DownloadManager ID of the currently downloading metadata to permanent storage.
- *
- * @param context to open shared prefs
- * @param uri the uri of the metadata
- * @param downloadId the id returned by DownloadManager
- */
- private static void writeMetadataDownloadId(final Context context, final String uri,
- final long downloadId) {
- MetadataDbHelper.registerMetadataDownloadId(context, uri, downloadId);
- }
-
- public static final int DOWNLOAD_OVER_METERED_SETTING_UNKNOWN = 0;
- public static final int DOWNLOAD_OVER_METERED_ALLOWED = 1;
- public static final int DOWNLOAD_OVER_METERED_DISALLOWED = 2;
-
- /**
- * Sets the setting that tells us whether we may download over a metered connection.
- */
- public static void setDownloadOverMeteredSetting(final Context context,
- final boolean shouldDownloadOverMetered) {
- final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
- final SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(DOWNLOAD_OVER_METERED_SETTING_PREFS_KEY, shouldDownloadOverMetered
- ? DOWNLOAD_OVER_METERED_ALLOWED : DOWNLOAD_OVER_METERED_DISALLOWED);
- editor.apply();
- }
-
- /**
- * Gets the setting that tells us whether we may download over a metered connection.
- *
- * This returns one of the constants above.
- */
- public static int getDownloadOverMeteredSetting(final Context context) {
- final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
- final int setting = prefs.getInt(DOWNLOAD_OVER_METERED_SETTING_PREFS_KEY,
- DOWNLOAD_OVER_METERED_SETTING_UNKNOWN);
- return setting;
- }
-
- /**
- * Download latest metadata from the server through DownloadManager for all known clients
- * @param context The context for retrieving resources
- * @return true if an update successfully started, false otherwise.
- */
- public static boolean tryUpdate(final Context context) {
- // TODO: loop through all clients instead of only doing the default one.
- final TreeSet<String> uris = new TreeSet<>();
- final Cursor cursor = MetadataDbHelper.queryClientIds(context);
- if (null == cursor) return false;
- try {
- if (!cursor.moveToFirst()) return false;
- do {
- final String clientId = cursor.getString(0);
- final String metadataUri =
- MetadataDbHelper.getMetadataUriAsString(context, clientId);
- PrivateLog.log("Update for clientId " + DebugLogUtils.s(clientId));
- DebugLogUtils.l("Update for clientId", clientId, " which uses URI ", metadataUri);
- uris.add(metadataUri);
- } while (cursor.moveToNext());
- } finally {
- cursor.close();
- }
- boolean started = false;
- for (final String metadataUri : uris) {
- if (!TextUtils.isEmpty(metadataUri)) {
- // If the metadata URI is empty, that means we should never update it at all.
- // It should not be possible to come here with a null metadata URI, because
- // it should have been rejected at the time of client registration; if there
- // is a bug and it happens anyway, doing nothing is the right thing to do.
- // For more information, {@see DictionaryProvider#insert(Uri, ContentValues)}.
- updateClientsWithMetadataUri(context, metadataUri);
- started = true;
- }
- }
- return started;
- }
-
- /**
- * Download latest metadata from the server through DownloadManager for all relevant clients
- *
- * @param context The context for retrieving resources
- * @param metadataUri The client to update
- */
- private static void updateClientsWithMetadataUri(
- final Context context, final String metadataUri) {
- Log.i(TAG, "updateClientsWithMetadataUri() : MetadataUri = " + metadataUri);
- // Adding a disambiguator to circumvent a bug in older versions of DownloadManager.
- // DownloadManager also stupidly cuts the extension to replace with its own that it
- // gets from the content-type. We need to circumvent this.
- final String disambiguator = "#" + System.currentTimeMillis()
- + ApplicationUtils.getVersionName(context) + ".json";
- final Request metadataRequest = new Request(Uri.parse(metadataUri + disambiguator));
- DebugLogUtils.l("Request =", metadataRequest);
-
- final Resources res = context.getResources();
- metadataRequest.setAllowedNetworkTypes(Request.NETWORK_WIFI | Request.NETWORK_MOBILE);
- metadataRequest.setTitle(res.getString(R.string.download_description));
- // Do not show the notification when downloading the metadata.
- metadataRequest.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
- metadataRequest.setVisibleInDownloadsUi(
- res.getBoolean(R.bool.metadata_downloads_visible_in_download_UI));
-
- final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
- if (maybeCancelUpdateAndReturnIfStillRunning(context, metadataUri, manager,
- DictionaryService.NO_CANCEL_DOWNLOAD_PERIOD_MILLIS)) {
- // We already have a recent download in progress. Don't register a new download.
- return;
- }
- final long downloadId;
- synchronized (sSharedIdProtector) {
- downloadId = manager.enqueue(metadataRequest);
- DebugLogUtils.l("Metadata download requested with id", downloadId);
- // If there is still a download in progress, it's been there for a while and
- // there is probably something wrong with download manager. It's best to just
- // overwrite the id and request it again. If the old one happens to finish
- // anyway, we don't know about its ID any more, so the downloadFinished
- // method will ignore it.
- writeMetadataDownloadId(context, metadataUri, downloadId);
- }
- Log.i(TAG, "updateClientsWithMetadataUri() : DownloadId = " + downloadId);
- }
-
- /**
- * Cancels downloading a file if there is one for this URI and it's too long.
- *
- * If we are not currently downloading the file at this URI, this is a no-op.
- *
- * @param context the context to open the database on
- * @param metadataUri the URI to cancel
- * @param manager an wrapped instance of DownloadManager
- * @param graceTime if there was a download started less than this many milliseconds, don't
- * cancel and return true
- * @return whether the download is still active
- */
- private static boolean maybeCancelUpdateAndReturnIfStillRunning(final Context context,
- final String metadataUri, final DownloadManagerWrapper manager, final long graceTime) {
- synchronized (sSharedIdProtector) {
- final DownloadIdAndStartDate metadataDownloadIdAndStartDate =
- MetadataDbHelper.getMetadataDownloadIdAndStartDateForURI(context, metadataUri);
- if (null == metadataDownloadIdAndStartDate) return false;
- if (NOT_AN_ID == metadataDownloadIdAndStartDate.mId) return false;
- if (metadataDownloadIdAndStartDate.mStartDate + graceTime
- > System.currentTimeMillis()) {
- return true;
- }
- manager.remove(metadataDownloadIdAndStartDate.mId);
- writeMetadataDownloadId(context, metadataUri, NOT_AN_ID);
- }
- // Consider a cancellation as a failure. As such, inform listeners that the download
- // has failed.
- for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
- listener.downloadedMetadata(false);
- }
- return false;
- }
-
- /**
- * Cancels a pending update for this client, if there is one.
- *
- * If we are not currently updating metadata for this client, this is a no-op. This is a helper
- * method that gets the download manager service and the metadata URI for this client.
- *
- * @param context the context, to get an instance of DownloadManager
- * @param clientId the ID of the client we want to cancel the update of
- */
- public static void cancelUpdate(final Context context, final String clientId) {
- final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
- final String metadataUri = MetadataDbHelper.getMetadataUriAsString(context, clientId);
- maybeCancelUpdateAndReturnIfStillRunning(context, metadataUri, manager, 0 /* graceTime */);
- }
-
- /**
- * Registers a download request and flags it as downloading in the metadata table.
- *
- * This is a helper method that exists to avoid race conditions where DownloadManager might
- * finish downloading the file before the data is committed to the database.
- * It registers the request with the DownloadManager service and also updates the metadata
- * database directly within a synchronized section.
- * This method has no intelligence about the data it commits to the database aside from the
- * download request id, which is not known before submitting the request to the download
- * manager. Hence, it only updates the relevant line.
- *
- * @param manager a wrapped download manager service to register the request with.
- * @param request the request to register.
- * @param db the metadata database.
- * @param id the id of the word list.
- * @param version the version of the word list.
- * @return the download id returned by the download manager.
- */
- public static long registerDownloadRequest(final DownloadManagerWrapper manager,
- final Request request, final SQLiteDatabase db, final String id, final int version) {
- Log.i(TAG, "registerDownloadRequest() : Id = " + id + " : Version = " + version);
- final long downloadId;
- synchronized (sSharedIdProtector) {
- downloadId = manager.enqueue(request);
- Log.i(TAG, "registerDownloadRequest() : DownloadId = " + downloadId);
- MetadataDbHelper.markEntryAsDownloading(db, id, version, downloadId);
- }
- return downloadId;
- }
-
- /**
- * Retrieve information about a specific download from DownloadManager.
- */
- private static CompletedDownloadInfo getCompletedDownloadInfo(
- final DownloadManagerWrapper manager, final long downloadId) {
- final Query query = new Query().setFilterById(downloadId);
- final Cursor cursor = manager.query(query);
-
- if (null == cursor) {
- return new CompletedDownloadInfo(null, downloadId, DownloadManager.STATUS_FAILED);
- }
- try {
- final String uri;
- final int status;
- if (cursor.moveToNext()) {
- final int columnStatus = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
- final int columnError = cursor.getColumnIndex(DownloadManager.COLUMN_REASON);
- final int columnUri = cursor.getColumnIndex(DownloadManager.COLUMN_URI);
- final int error = cursor.getInt(columnError);
- status = cursor.getInt(columnStatus);
- final String uriWithAnchor = cursor.getString(columnUri);
- int anchorIndex = uriWithAnchor.indexOf('#');
- if (anchorIndex != -1) {
- uri = uriWithAnchor.substring(0, anchorIndex);
- } else {
- uri = uriWithAnchor;
- }
- if (DownloadManager.STATUS_SUCCESSFUL != status) {
- Log.e(TAG, "Permanent failure of download " + downloadId
- + " with error code: " + error);
- }
- } else {
- uri = null;
- status = DownloadManager.STATUS_FAILED;
- }
- return new CompletedDownloadInfo(uri, downloadId, status);
- } finally {
- cursor.close();
- }
- }
-
- private static ArrayList<DownloadRecord> getDownloadRecordsForCompletedDownloadInfo(
- final Context context, final CompletedDownloadInfo downloadInfo) {
- // Get and check the ID of the file we are waiting for, compare them to downloaded ones
- synchronized(sSharedIdProtector) {
- final ArrayList<DownloadRecord> downloadRecords =
- MetadataDbHelper.getDownloadRecordsForDownloadId(context,
- downloadInfo.mDownloadId);
- // If any of these is metadata, we should update the DB
- boolean hasMetadata = false;
- for (DownloadRecord record : downloadRecords) {
- if (record.isMetadata()) {
- hasMetadata = true;
- break;
- }
- }
- if (hasMetadata) {
- writeMetadataDownloadId(context, downloadInfo.mUri, NOT_AN_ID);
- MetadataDbHelper.saveLastUpdateTimeOfUri(context, downloadInfo.mUri);
- }
- return downloadRecords;
- }
- }
-
- /**
- * Take appropriate action after a download finished, in success or in error.
- *
- * This is called by the system upon broadcast from the DownloadManager that a file
- * has been downloaded successfully.
- * After a simple check that this is actually the file we are waiting for, this
- * method basically coordinates the parsing and comparison of metadata, and fires
- * the computation of the list of actions that should be taken then executes them.
- *
- * @param context The context for this action.
- * @param intent The intent from the DownloadManager containing details about the download.
- */
- /* package */ static void downloadFinished(final Context context, final Intent intent) {
- // Get and check the ID of the file that was downloaded
- final long fileId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, NOT_AN_ID);
- Log.i(TAG, "downloadFinished() : DownloadId = " + fileId);
- if (NOT_AN_ID == fileId) return; // Spurious wake-up: ignore
-
- final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
- final CompletedDownloadInfo downloadInfo = getCompletedDownloadInfo(manager, fileId);
-
- final ArrayList<DownloadRecord> recordList =
- getDownloadRecordsForCompletedDownloadInfo(context, downloadInfo);
- if (null == recordList) return; // It was someone else's download.
- DebugLogUtils.l("Received result for download ", fileId);
-
- // TODO: handle gracefully a null pointer here. This is practically impossible because
- // we come here only when DownloadManager explicitly called us when it ended a
- // download, so we are pretty sure it's alive. It's theoretically possible that it's
- // disabled right inbetween the firing of the intent and the control reaching here.
-
- for (final DownloadRecord record : recordList) {
- // downloadSuccessful is not final because we may still have exceptions from now on
- boolean downloadSuccessful = false;
- try {
- if (downloadInfo.wasSuccessful()) {
- downloadSuccessful = handleDownloadedFile(context, record, manager, fileId);
- Log.i(TAG, "downloadFinished() : Success = " + downloadSuccessful);
- }
- } finally {
- final String resultMessage = downloadSuccessful ? "Success" : "Failure";
- if (record.isMetadata()) {
- Log.i(TAG, "downloadFinished() : Metadata " + resultMessage);
- publishUpdateMetadataCompleted(context, downloadSuccessful);
- } else {
- Log.i(TAG, "downloadFinished() : WordList " + resultMessage);
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, record.mClientId);
- publishUpdateWordListCompleted(context, downloadSuccessful, fileId,
- db, record.mAttributes, record.mClientId);
- }
- }
- }
- // Now that we're done using it, we can remove this download from DLManager
- manager.remove(fileId);
- }
-
- /**
- * Sends a broadcast informing listeners that the dictionaries were updated.
- *
- * This will call all local listeners through the UpdateEventListener#downloadedMetadata
- * callback (for example, the dictionary provider interface uses this to stop the Loading
- * animation) and send a broadcast about the metadata having been updated. For a client of
- * the dictionary pack like Latin IME, this means it should re-query the dictionary pack
- * for any relevant new data.
- *
- * @param context the context, to send the broadcast.
- * @param downloadSuccessful whether the download of the metadata was successful or not.
- */
- public static void publishUpdateMetadataCompleted(final Context context,
- final boolean downloadSuccessful) {
- // We need to warn all listeners of what happened. But some listeners may want to
- // remove themselves or re-register something in response. Hence we should take a
- // snapshot of the listener list and warn them all. This also prevents any
- // concurrent modification problem of the static list.
- for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
- listener.downloadedMetadata(downloadSuccessful);
- }
- publishUpdateCycleCompletedEvent(context);
- }
-
- private static void publishUpdateWordListCompleted(final Context context,
- final boolean downloadSuccessful, final long fileId,
- final SQLiteDatabase db, final ContentValues downloadedFileRecord,
- final String clientId) {
- synchronized(sSharedIdProtector) {
- if (downloadSuccessful) {
- final ActionBatch actions = new ActionBatch();
- actions.add(new ActionBatch.InstallAfterDownloadAction(clientId,
- downloadedFileRecord));
- actions.execute(context, new LogProblemReporter(TAG));
- } else {
- MetadataDbHelper.deleteDownloadingEntry(db, fileId);
- }
- }
- // See comment above about #linkedCopyOfLists
- for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
- listener.wordListDownloadFinished(downloadedFileRecord.getAsString(
- MetadataDbHelper.WORDLISTID_COLUMN), downloadSuccessful);
- }
- publishUpdateCycleCompletedEvent(context);
- }
-
- private static void publishUpdateCycleCompletedEvent(final Context context) {
- // Even if this is not successful, we have to publish the new state.
- PrivateLog.log("Publishing update cycle completed event");
- DebugLogUtils.l("Publishing update cycle completed event");
- for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
- listener.updateCycleCompleted();
- }
- signalNewDictionaryState(context);
- }
-
- private static boolean handleDownloadedFile(final Context context,
- final DownloadRecord downloadRecord, final DownloadManagerWrapper manager,
- final long fileId) {
- try {
- // {@link handleWordList(Context,InputStream,ContentValues)}.
- // Handle the downloaded file according to its type
- if (downloadRecord.isMetadata()) {
- DebugLogUtils.l("Data D/L'd is metadata for", downloadRecord.mClientId);
- // #handleMetadata() closes its InputStream argument
- handleMetadata(context, new ParcelFileDescriptor.AutoCloseInputStream(
- manager.openDownloadedFile(fileId)), downloadRecord.mClientId);
- } else {
- DebugLogUtils.l("Data D/L'd is a word list");
- final int wordListStatus = downloadRecord.mAttributes.getAsInteger(
- MetadataDbHelper.STATUS_COLUMN);
- if (MetadataDbHelper.STATUS_DOWNLOADING == wordListStatus) {
- // #handleWordList() closes its InputStream argument
- handleWordList(context, new ParcelFileDescriptor.AutoCloseInputStream(
- manager.openDownloadedFile(fileId)), downloadRecord);
- } else {
- Log.e(TAG, "Spurious download ended. Maybe a cancelled download?");
- }
- }
- return true;
- } catch (FileNotFoundException e) {
- Log.e(TAG, "A file was downloaded but it can't be opened", e);
- } catch (IOException e) {
- // Can't read the file... disk damage?
- Log.e(TAG, "Can't read a file", e);
- // TODO: Check with UX how we should warn the user.
- } catch (IllegalStateException e) {
- // The format of the downloaded file is incorrect. We should maybe report upstream?
- Log.e(TAG, "Incorrect data received", e);
- } catch (BadFormatException e) {
- // The format of the downloaded file is incorrect. We should maybe report upstream?
- Log.e(TAG, "Incorrect data received", e);
- }
- return false;
- }
-
- /**
- * Returns a copy of the specified list, with all elements copied.
- *
- * This returns a linked list.
- */
- private static <T> List<T> linkedCopyOfList(final List<T> src) {
- // Instantiation of a parameterized type is not possible in Java, so it's not possible to
- // return the same type of list that was passed - probably the same reason why Collections
- // does not do it. So we need to decide statically which concrete type to return.
- return new LinkedList<>(src);
- }
-
- /**
- * Warn Android Keyboard that the state of dictionaries changed and it should refresh its data.
- */
- private static void signalNewDictionaryState(final Context context) {
- // TODO: Also provide the locale of the updated dictionary so that the LatinIme
- // does not have to reset if it is a different locale.
- final Intent newDictBroadcast =
- new Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
- context.sendBroadcast(newDictBroadcast);
- }
-
- /**
- * Parse metadata and take appropriate action (that is, upgrade dictionaries).
- * @param context the context to read settings.
- * @param stream an input stream pointing to the downloaded data. May not be null.
- * Will be closed upon finishing.
- * @param clientId the ID of the client to update
- * @throws BadFormatException if the metadata is not in a known format.
- * @throws IOException if the downloaded file can't be read from the disk
- */
- public static void handleMetadata(final Context context, final InputStream stream,
- final String clientId) throws IOException, BadFormatException {
- DebugLogUtils.l("Entering handleMetadata");
- final List<WordListMetadata> newMetadata;
- final InputStreamReader reader = new InputStreamReader(stream);
- try {
- // According to the doc InputStreamReader buffers, so no need to add a buffering layer
- newMetadata = MetadataHandler.readMetadata(reader);
- } finally {
- reader.close();
- }
-
- DebugLogUtils.l("Downloaded metadata :", newMetadata);
- PrivateLog.log("Downloaded metadata\n" + newMetadata);
-
- final ActionBatch actions = computeUpgradeTo(context, clientId, newMetadata);
- // TODO: Check with UX how we should report to the user
- // TODO: add an action to close the database
- actions.execute(context, new LogProblemReporter(TAG));
- }
-
- /**
- * Handle a word list: put it in its right place, and update the passed content values.
- * @param context the context for opening files.
- * @param inputStream an input stream pointing to the downloaded data. May not be null.
- * Will be closed upon finishing.
- * @param downloadRecord the content values to fill the file name in.
- * @throws IOException if files can't be read or written.
- * @throws BadFormatException if the md5 checksum doesn't match the metadata.
- */
- private static void handleWordList(final Context context,
- final InputStream inputStream, final DownloadRecord downloadRecord)
- throws IOException, BadFormatException {
-
- // DownloadManager does not have the ability to put the file directly where we want
- // it, so we had it download to a temporary place. Now we move it. It will be deleted
- // automatically by DownloadManager.
- DebugLogUtils.l("Downloaded a new word list :", downloadRecord.mAttributes.getAsString(
- MetadataDbHelper.DESCRIPTION_COLUMN), "for", downloadRecord.mClientId);
- PrivateLog.log("Downloaded a new word list with description : "
- + downloadRecord.mAttributes.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN)
- + " for " + downloadRecord.mClientId);
-
- final String locale =
- downloadRecord.mAttributes.getAsString(MetadataDbHelper.LOCALE_COLUMN);
- final String destinationFile = getTempFileName(context, locale);
- downloadRecord.mAttributes.put(MetadataDbHelper.LOCAL_FILENAME_COLUMN, destinationFile);
-
- FileOutputStream outputStream = null;
- try {
- outputStream = context.openFileOutput(destinationFile, Context.MODE_PRIVATE);
- copyFile(inputStream, outputStream);
- } finally {
- inputStream.close();
- if (outputStream != null) {
- outputStream.close();
- }
- }
-
- // TODO: Consolidate this MD5 calculation with file copying above.
- // We need to reopen the file because the inputstream bytes have been consumed, and there
- // is nothing in InputStream to reopen or rewind the stream
- FileInputStream copiedFile = null;
- final String md5sum;
- try {
- copiedFile = context.openFileInput(destinationFile);
- md5sum = MD5Calculator.checksum(copiedFile);
- } finally {
- if (copiedFile != null) {
- copiedFile.close();
- }
- }
- if (TextUtils.isEmpty(md5sum)) {
- return; // We can't compute the checksum anyway, so return and hope for the best
- }
- if (!md5sum.equals(downloadRecord.mAttributes.getAsString(
- MetadataDbHelper.CHECKSUM_COLUMN))) {
- context.deleteFile(destinationFile);
- throw new BadFormatException("MD5 checksum check failed : \"" + md5sum + "\" <> \""
- + downloadRecord.mAttributes.getAsString(MetadataDbHelper.CHECKSUM_COLUMN)
- + "\"");
- }
- }
-
- /**
- * Copies in to out using FileChannels.
- *
- * This tries to use channels for fast copying. If it doesn't work, fall back to
- * copyFileFallBack below.
- *
- * @param in the stream to copy from.
- * @param out the stream to copy to.
- * @throws IOException if both the normal and fallback methods raise exceptions.
- */
- private static void copyFile(final InputStream in, final OutputStream out)
- throws IOException {
- DebugLogUtils.l("Copying files");
- if (!(in instanceof FileInputStream) || !(out instanceof FileOutputStream)) {
- DebugLogUtils.l("Not the right types");
- copyFileFallback(in, out);
- } else {
- try {
- final FileChannel sourceChannel = ((FileInputStream) in).getChannel();
- final FileChannel destinationChannel = ((FileOutputStream) out).getChannel();
- sourceChannel.transferTo(0, Integer.MAX_VALUE, destinationChannel);
- } catch (IOException e) {
- // Can't work with channels, or something went wrong. Copy by hand.
- DebugLogUtils.l("Won't work");
- copyFileFallback(in, out);
- }
- }
- }
-
- /**
- * Copies in to out with read/write methods, not FileChannels.
- *
- * @param in the stream to copy from.
- * @param out the stream to copy to.
- * @throws IOException if a read or a write fails.
- */
- private static void copyFileFallback(final InputStream in, final OutputStream out)
- throws IOException {
- DebugLogUtils.l("Falling back to slow copy");
- final byte[] buffer = new byte[FILE_COPY_BUFFER_SIZE];
- for (int readBytes = in.read(buffer); readBytes >= 0; readBytes = in.read(buffer))
- out.write(buffer, 0, readBytes);
- }
-
- /**
- * Creates and returns a new file to store a dictionary
- * @param context the context to use to open the file.
- * @param locale the locale for this dictionary, to make the file name more readable.
- * @return the file name, or throw an exception.
- * @throws IOException if the file cannot be created.
- */
- private static String getTempFileName(final Context context, final String locale)
- throws IOException {
- DebugLogUtils.l("Entering openTempFileOutput");
- final File dir = context.getFilesDir();
- final File f = File.createTempFile(locale + TEMP_DICT_FILE_SUB, DICT_FILE_SUFFIX, dir);
- DebugLogUtils.l("File name is", f.getName());
- return f.getName();
- }
-
- /**
- * Compare metadata (collections of word lists).
- *
- * This method takes whole metadata sets directly and compares them, matching the wordlists in
- * each of them on the id. It creates an ActionBatch object that can be .execute()'d to perform
- * the actual upgrade from `from' to `to'.
- *
- * @param context the context to open databases on.
- * @param clientId the id of the client.
- * @param from the dictionary descriptor (as a list of wordlists) to upgrade from.
- * @param to the dictionary descriptor (as a list of wordlists) to upgrade to.
- * @return an ordered list of runnables to be called to upgrade.
- */
- private static ActionBatch compareMetadataForUpgrade(final Context context,
- final String clientId, @Nullable final List<WordListMetadata> from,
- @Nullable final List<WordListMetadata> to) {
- final ActionBatch actions = new ActionBatch();
- // Upgrade existing word lists
- DebugLogUtils.l("Comparing dictionaries");
- final Set<String> wordListIds = new TreeSet<>();
- // TODO: Can these be null?
- final List<WordListMetadata> fromList = (from == null) ? new ArrayList<WordListMetadata>()
- : from;
- final List<WordListMetadata> toList = (to == null) ? new ArrayList<WordListMetadata>()
- : to;
- for (WordListMetadata wlData : fromList) wordListIds.add(wlData.mId);
- for (WordListMetadata wlData : toList) wordListIds.add(wlData.mId);
- for (String id : wordListIds) {
- final WordListMetadata currentInfo = MetadataHandler.findWordListById(fromList, id);
- final WordListMetadata metadataInfo = MetadataHandler.findWordListById(toList, id);
- // TODO: Remove the following unnecessary check, since we are now doing the filtering
- // inside findWordListById.
- final WordListMetadata newInfo = null == metadataInfo
- || metadataInfo.mFormatVersion > MAXIMUM_SUPPORTED_FORMAT_VERSION
- ? null : metadataInfo;
- DebugLogUtils.l("Considering updating ", id, "currentInfo =", currentInfo);
-
- if (null == currentInfo && null == newInfo) {
- // This may happen if a new word list appeared that we can't handle.
- if (null == metadataInfo) {
- // What happened? Bug in Set<>?
- Log.e(TAG, "Got an id for a wordlist that is neither in from nor in to");
- } else {
- // We may come here if there is a new word list that we can't handle.
- Log.i(TAG, "Can't handle word list with id '" + id + "' because it has format"
- + " version " + metadataInfo.mFormatVersion + " and the maximum version"
- + " we can handle is " + MAXIMUM_SUPPORTED_FORMAT_VERSION);
- }
- continue;
- } else if (null == currentInfo) {
- // This is the case where a new list that we did not know of popped on the server.
- // Make it available.
- actions.add(new ActionBatch.MakeAvailableAction(clientId, newInfo));
- } else if (null == newInfo) {
- // This is the case where an old list we had is not in the server data any more.
- // Pass false to ForgetAction: this may be installed and we still want to apply
- // a forget-like action (remove the URL) if it is, so we want to turn off the
- // status == AVAILABLE check. If it's DELETING, this is the right thing to do,
- // as we want to leave the record as long as Android Keyboard has not deleted it ;
- // the record will be removed when the file is actually deleted.
- actions.add(new ActionBatch.ForgetAction(clientId, currentInfo, false));
- } else {
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
- if (newInfo.mVersion == currentInfo.mVersion) {
- if (TextUtils.equals(newInfo.mRemoteFilename, currentInfo.mRemoteFilename)) {
- // If the dictionary url hasn't changed, we should preserve the retryCount.
- newInfo.mRetryCount = currentInfo.mRetryCount;
- }
- // If it's the same id/version, we update the DB with the new values.
- // It doesn't matter too much if they didn't change.
- actions.add(new ActionBatch.UpdateDataAction(clientId, newInfo));
- } else if (newInfo.mVersion > currentInfo.mVersion) {
- // If it's a new version, it's a different entry in the database. Make it
- // available, and if it's installed, also start the download.
- final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
- currentInfo.mId, currentInfo.mVersion);
- final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
- actions.add(new ActionBatch.MakeAvailableAction(clientId, newInfo));
- if (status == MetadataDbHelper.STATUS_INSTALLED
- || status == MetadataDbHelper.STATUS_DISABLED) {
- actions.add(new ActionBatch.StartDownloadAction(clientId, newInfo));
- } else {
- // Pass true to ForgetAction: this is indeed an update to a non-installed
- // word list, so activate status == AVAILABLE check
- // In case the status is DELETING, this is the right thing to do. It will
- // leave the entry as DELETING and remove its URL so that Android Keyboard
- // can delete it the next time it starts up.
- actions.add(new ActionBatch.ForgetAction(clientId, currentInfo, true));
- }
- } else if (DEBUG) {
- Log.i(TAG, "Not updating word list " + id
- + " : current list timestamp is " + currentInfo.mLastUpdate
- + " ; new list timestamp is " + newInfo.mLastUpdate);
- }
- }
- }
- return actions;
- }
-
- /**
- * Computes an upgrade from the current state of the dictionaries to some desired state.
- * @param context the context for reading settings and files.
- * @param clientId the id of the client.
- * @param newMetadata the state we want to upgrade to.
- * @return the upgrade from the current state to the desired state, ready to be executed.
- */
- public static ActionBatch computeUpgradeTo(final Context context, final String clientId,
- final List<WordListMetadata> newMetadata) {
- final List<WordListMetadata> currentMetadata =
- MetadataHandler.getCurrentMetadata(context, clientId);
- return compareMetadataForUpgrade(context, clientId, currentMetadata, newMetadata);
- }
-
- /**
- * Installs a word list if it has never been requested.
- *
- * This is called when a word list is requested, and is available but not installed. It checks
- * the conditions for auto-installation: if the dictionary is a main dictionary for this
- * language, and it has never been opted out through the dictionary interface, then we start
- * installing it. For the user who enables a language and uses it for the first time, the
- * dictionary should magically start being used a short time after they start typing.
- * The mayPrompt argument indicates whether we should prompt the user for a decision to
- * download or not, in case we decide we are in the case where we should download - this
- * roughly happens when the current connectivity is 3G. See
- * DictionaryProvider#getDictionaryWordListsForContentUri for details.
- */
- // As opposed to many other methods, this method does not need the version of the word
- // list because it may only install the latest version we know about for this specific
- // word list ID / client ID combination.
- public static void installIfNeverRequested(final Context context, final String clientId,
- final String wordlistId) {
- Log.i(TAG, "installIfNeverRequested() : ClientId = " + clientId
- + " : WordListId = " + wordlistId);
- final String[] idArray = wordlistId.split(DictionaryProvider.ID_CATEGORY_SEPARATOR);
- // If we have a new-format dictionary id (category:manual_id), then use the
- // specified category. Otherwise, it is a main dictionary, so force the
- // MAIN category upon it.
- final String category = 2 == idArray.length ? idArray[0] : MAIN_DICTIONARY_CATEGORY;
- if (!MAIN_DICTIONARY_CATEGORY.equals(category)) {
- // Not a main dictionary. We only auto-install main dictionaries, so we can return now.
- return;
- }
- if (CommonPreferences.getCommonPreferences(context).contains(wordlistId)) {
- // If some kind of settings has been done in the past for this specific id, then
- // this is not a candidate for auto-install. Because it already is either true,
- // in which case it may be installed or downloading or whatever, and we don't
- // need to care about it because it's already handled or being handled, or it's false
- // in which case it means the user explicitely turned it off and don't want to have
- // it installed. So we quit right away.
- return;
- }
-
- final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId);
- final ContentValues installCandidate =
- MetadataDbHelper.getContentValuesOfLatestAvailableWordlistById(db, wordlistId);
- if (MetadataDbHelper.STATUS_AVAILABLE
- != installCandidate.getAsInteger(MetadataDbHelper.STATUS_COLUMN)) {
- // If it's not "AVAILABLE", we want to stop now. Because candidates for auto-install
- // are lists that we know are available, but we also know have never been installed.
- // It does obviously not concern already installed lists, or downloading lists,
- // or those that have been disabled, flagged as deleting... So anything else than
- // AVAILABLE means we don't auto-install.
- return;
- }
-
- // We decided against prompting the user for a decision. This may be because we were
- // explicitly asked not to, or because we are currently on wi-fi anyway, or because we
- // already know the answer to the question. We'll enqueue a request ; StartDownloadAction
- // knows to use the correct type of network according to the current settings.
-
- // Also note that once it's auto-installed, a word list will be marked as INSTALLED. It will
- // thus receive automatic updates if there are any, which is what we want. If the user does
- // not want this word list, they will have to go to the settings and change them, which will
- // change the shared preferences. So there is no way for a word list that has been
- // auto-installed once to get auto-installed again, and that's what we want.
- final ActionBatch actions = new ActionBatch();
- WordListMetadata metadata = WordListMetadata.createFromContentValues(installCandidate);
- actions.add(new ActionBatch.StartDownloadAction(clientId, metadata));
- final String localeString = installCandidate.getAsString(MetadataDbHelper.LOCALE_COLUMN);
-
- // We are in a content provider: we can't do any UI at all. We have to defer the displaying
- // itself to the service. Also, we only display this when the user does not have a
- // dictionary for this language already. During setup wizard, however, this UI is
- // suppressed.
- final boolean deviceProvisioned = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- if (deviceProvisioned) {
- final Intent intent = new Intent();
- intent.setClass(context, DictionaryService.class);
- intent.setAction(DictionaryService.SHOW_DOWNLOAD_TOAST_INTENT_ACTION);
- intent.putExtra(DictionaryService.LOCALE_INTENT_ARGUMENT, localeString);
- context.startService(intent);
- } else {
- Log.i(TAG, "installIfNeverRequested() : Don't show download toast");
- }
-
- Log.i(TAG, "installIfNeverRequested() : StartDownloadAction for " + metadata);
- actions.execute(context, new LogProblemReporter(TAG));
- }
-
- /**
- * Marks the word list with the passed id as used.
- *
- * This will download/install the list as required. The action will see that the destination
- * word list is a valid list, and take appropriate action - in this case, mark it as used.
- * @see ActionBatch.Action#execute
- *
- * @param context the context for using action batches.
- * @param clientId the id of the client.
- * @param wordlistId the id of the word list to mark as installed.
- * @param version the version of the word list to mark as installed.
- * @param status the current status of the word list.
- * @param allowDownloadOnMeteredData whether to download even on metered data connection
- */
- // The version argument is not used yet, because we don't need it to retrieve the information
- // we need. However, the pair (id, version) being the primary key to a word list in the database
- // it feels better for consistency to pass it, and some methods retrieving information about a
- // word list need it so we may need it in the future.
- public static void markAsUsed(final Context context, final String clientId,
- final String wordlistId, final int version,
- final int status, final boolean allowDownloadOnMeteredData) {
- final WordListMetadata wordListMetaData = MetadataHandler.getCurrentMetadataForWordList(
- context, clientId, wordlistId, version);
-
- if (null == wordListMetaData) return;
-
- final ActionBatch actions = new ActionBatch();
- if (MetadataDbHelper.STATUS_DISABLED == status
- || MetadataDbHelper.STATUS_DELETING == status) {
- actions.add(new ActionBatch.EnableAction(clientId, wordListMetaData));
- } else if (MetadataDbHelper.STATUS_AVAILABLE == status) {
- actions.add(new ActionBatch.StartDownloadAction(clientId, wordListMetaData));
- } else {
- Log.e(TAG, "Unexpected state of the word list for markAsUsed : " + status);
- }
- actions.execute(context, new LogProblemReporter(TAG));
- signalNewDictionaryState(context);
- }
-
- /**
- * Marks the word list with the passed id as unused.
- *
- * This leaves the file on the disk for ulterior use. The action will see that the destination
- * word list is null, and take appropriate action - in this case, mark it as unused.
- * @see ActionBatch.Action#execute
- *
- * @param context the context for using action batches.
- * @param clientId the id of the client.
- * @param wordlistId the id of the word list to mark as installed.
- * @param version the version of the word list to mark as installed.
- * @param status the current status of the word list.
- */
- // The version and status arguments are not used yet, but this method matches its interface to
- // markAsUsed for consistency.
- public static void markAsUnused(final Context context, final String clientId,
- final String wordlistId, final int version, final int status) {
-
- final WordListMetadata wordListMetaData = MetadataHandler.getCurrentMetadataForWordList(
- context, clientId, wordlistId, version);
-
- if (null == wordListMetaData) return;
- final ActionBatch actions = new ActionBatch();
- actions.add(new ActionBatch.DisableAction(clientId, wordListMetaData));
- actions.execute(context, new LogProblemReporter(TAG));
- signalNewDictionaryState(context);
- }
-
- /**
- * Marks the word list with the passed id as deleting.
- *
- * This basically means that on the next chance there is (right away if Android Keyboard
- * happens to be up, or the next time it gets up otherwise) the dictionary pack will
- * supply an empty dictionary to it that will replace whatever dictionary is installed.
- * This allows to release the space taken by a dictionary (except for the few bytes the
- * empty dictionary takes up), and override a built-in default dictionary so that we
- * can fake delete a built-in dictionary.
- *
- * @param context the context to open the database on.
- * @param clientId the id of the client.
- * @param wordlistId the id of the word list to mark as deleted.
- * @param version the version of the word list to mark as deleted.
- * @param status the current status of the word list.
- */
- public static void markAsDeleting(final Context context, final String clientId,
- final String wordlistId, final int version, final int status) {
-
- final WordListMetadata wordListMetaData = MetadataHandler.getCurrentMetadataForWordList(
- context, clientId, wordlistId, version);
-
- if (null == wordListMetaData) return;
- final ActionBatch actions = new ActionBatch();
- actions.add(new ActionBatch.DisableAction(clientId, wordListMetaData));
- actions.add(new ActionBatch.StartDeleteAction(clientId, wordListMetaData));
- actions.execute(context, new LogProblemReporter(TAG));
- signalNewDictionaryState(context);
- }
-
- /**
- * Marks the word list with the passed id as actually deleted.
- *
- * This reverts to available status or deletes the row as appropriate.
- *
- * @param context the context to open the database on.
- * @param clientId the id of the client.
- * @param wordlistId the id of the word list to mark as deleted.
- * @param version the version of the word list to mark as deleted.
- * @param status the current status of the word list.
- */
- public static void markAsDeleted(final Context context, final String clientId,
- final String wordlistId, final int version, final int status) {
- final WordListMetadata wordListMetaData = MetadataHandler.getCurrentMetadataForWordList(
- context, clientId, wordlistId, version);
-
- if (null == wordListMetaData) return;
-
- final ActionBatch actions = new ActionBatch();
- actions.add(new ActionBatch.FinishDeleteAction(clientId, wordListMetaData));
- actions.execute(context, new LogProblemReporter(TAG));
- signalNewDictionaryState(context);
- }
-
- /**
- * Checks whether the word list should be downloaded again; in which case an download &
- * installation attempt is made. Otherwise the word list is marked broken.
- *
- * @param context the context to open the database on.
- * @param clientId the id of the client.
- * @param wordlistId the id of the word list which is broken.
- * @param version the version of the broken word list.
- */
- public static void markAsBrokenOrRetrying(final Context context, final String clientId,
- final String wordlistId, final int version) {
- boolean isRetryPossible = MetadataDbHelper.maybeMarkEntryAsRetrying(
- MetadataDbHelper.getDb(context, clientId), wordlistId, version);
-
- if (isRetryPossible) {
- if (DEBUG) {
- Log.d(TAG, "Attempting to download & install the wordlist again.");
- }
- final WordListMetadata wordListMetaData = MetadataHandler.getCurrentMetadataForWordList(
- context, clientId, wordlistId, version);
- if (wordListMetaData == null) {
- return;
- }
-
- final ActionBatch actions = new ActionBatch();
- actions.add(new ActionBatch.StartDownloadAction(clientId, wordListMetaData));
- actions.execute(context, new LogProblemReporter(TAG));
- } else {
- if (DEBUG) {
- Log.d(TAG, "Retries for wordlist exhausted, deleting the wordlist from table.");
- }
- MetadataDbHelper.deleteEntry(MetadataDbHelper.getDb(context, clientId),
- wordlistId, version);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java b/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java
deleted file mode 100644
index 99cffb816..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.ContentValues;
-
-import javax.annotation.Nonnull;
-
-/**
- * The metadata for a single word list.
- *
- * Instances of this class are always immutable.
- */
-public class WordListMetadata {
-
- public final String mId;
- public final int mType; // Type, as of MetadataDbHelper#TYPE_*
- public final String mDescription;
- public final long mLastUpdate;
- public final long mFileSize;
- public final String mRawChecksum;
- public final String mChecksum;
- public final String mLocalFilename;
- public final String mRemoteFilename;
- public final int mVersion; // version of this word list
- public final int mFlags; // Always 0 in this version, reserved for future use
- public int mRetryCount;
-
- // The locale is matched against the locale requested by the client. The matching algorithm
- // is a standard locale matching with fallback; it is implemented in
- // DictionaryProvider#getDictionaryFileForContentUri.
- public final String mLocale;
-
-
- // Version number of the format.
- // This implementation of the DictionaryDataService knows how to handle format 1 only.
- // This is only for forward compatibility, to be able to upgrade the format without
- // breaking old implementations.
- public final int mFormatVersion;
-
- public WordListMetadata(final String id, final int type,
- final String description, final long lastUpdate, final long fileSize,
- final String rawChecksum, final String checksum, final int retryCount,
- final String localFilename, final String remoteFilename,
- final int version, final int formatVersion,
- final int flags, final String locale) {
- mId = id;
- mType = type;
- mDescription = description;
- mLastUpdate = lastUpdate; // In milliseconds
- mFileSize = fileSize;
- mRawChecksum = rawChecksum;
- mChecksum = checksum;
- mRetryCount = retryCount;
- mLocalFilename = localFilename;
- mRemoteFilename = remoteFilename;
- mVersion = version;
- mFormatVersion = formatVersion;
- mFlags = flags;
- mLocale = locale;
- }
-
- /**
- * Create a WordListMetadata from the contents of a ContentValues.
- *
- * If this lacks any required field, IllegalArgumentException is thrown.
- */
- public static WordListMetadata createFromContentValues(@Nonnull final ContentValues values) {
- final String id = values.getAsString(MetadataDbHelper.WORDLISTID_COLUMN);
- final Integer type = values.getAsInteger(MetadataDbHelper.TYPE_COLUMN);
- final String description = values.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN);
- final Long lastUpdate = values.getAsLong(MetadataDbHelper.DATE_COLUMN);
- final Long fileSize = values.getAsLong(MetadataDbHelper.FILESIZE_COLUMN);
- final String rawChecksum = values.getAsString(MetadataDbHelper.RAW_CHECKSUM_COLUMN);
- final String checksum = values.getAsString(MetadataDbHelper.CHECKSUM_COLUMN);
- final int retryCount = values.getAsInteger(MetadataDbHelper.RETRY_COUNT_COLUMN);
- final String localFilename = values.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
- final String remoteFilename = values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
- final Integer version = values.getAsInteger(MetadataDbHelper.VERSION_COLUMN);
- final Integer formatVersion = values.getAsInteger(MetadataDbHelper.FORMATVERSION_COLUMN);
- final Integer flags = values.getAsInteger(MetadataDbHelper.FLAGS_COLUMN);
- final String locale = values.getAsString(MetadataDbHelper.LOCALE_COLUMN);
- if (null == id
- || null == type
- || null == description
- || null == lastUpdate
- || null == fileSize
- || null == checksum
- || null == localFilename
- || null == remoteFilename
- || null == version
- || null == formatVersion
- || null == flags
- || null == locale) {
- throw new IllegalArgumentException();
- }
- return new WordListMetadata(id, type, description, lastUpdate, fileSize, rawChecksum,
- checksum, retryCount, localFilename, remoteFilename, version, formatVersion,
- flags, locale);
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(WordListMetadata.class.getSimpleName());
- sb.append(" : ").append(mId);
- sb.append("\nType : ").append(mType);
- sb.append("\nDescription : ").append(mDescription);
- sb.append("\nLastUpdate : ").append(mLastUpdate);
- sb.append("\nFileSize : ").append(mFileSize);
- sb.append("\nRawChecksum : ").append(mRawChecksum);
- sb.append("\nChecksum : ").append(mChecksum);
- sb.append("\nRetryCount: ").append(mRetryCount);
- sb.append("\nLocalFilename : ").append(mLocalFilename);
- sb.append("\nRemoteFilename : ").append(mRemoteFilename);
- sb.append("\nVersion : ").append(mVersion);
- sb.append("\nFormatVersion : ").append(mFormatVersion);
- sb.append("\nFlags : ").append(mFlags);
- sb.append("\nLocale : ").append(mLocale);
- return sb.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java
deleted file mode 100644
index 500e39e0e..000000000
--- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.inputmethod.dictionarypack;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.Preference;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.android.inputmethod.latin.R;
-
-import java.util.Locale;
-
-/**
- * A preference for one word list.
- *
- * This preference refers to a single word list, as available in the dictionary
- * pack. Upon being pressed, it displays a menu to allow the user to install, disable,
- * enable or delete it as appropriate for the current state of the word list.
- */
-public final class WordListPreference extends Preference {
- private static final String TAG = WordListPreference.class.getSimpleName();
-
- // What to display in the "status" field when we receive unknown data as a status from
- // the content provider. Empty string sounds sensible.
- private static final String NO_STATUS_MESSAGE = "";
-
- /// Actions
- private static final int ACTION_UNKNOWN = 0;
- private static final int ACTION_ENABLE_DICT = 1;
- private static final int ACTION_DISABLE_DICT = 2;
- private static final int ACTION_DELETE_DICT = 3;
-
- // Members
- // The metadata word list id and version of this word list.
- public final String mWordlistId;
- public final int mVersion;
- public final Locale mLocale;
- public final String mDescription;
-
- // The id of the client for which this preference is.
- private final String mClientId;
- // The status
- private int mStatus;
- // The size of the dictionary file
- private final int mFilesize;
-
- private final DictionaryListInterfaceState mInterfaceState;
-
- public WordListPreference(final Context context,
- final DictionaryListInterfaceState dictionaryListInterfaceState, final String clientId,
- final String wordlistId, final int version, final Locale locale,
- final String description, final int status, final int filesize) {
- super(context, null);
- mInterfaceState = dictionaryListInterfaceState;
- mClientId = clientId;
- mVersion = version;
- mWordlistId = wordlistId;
- mFilesize = filesize;
- mLocale = locale;
- mDescription = description;
-
- setLayoutResource(R.layout.dictionary_line);
-
- setTitle(description);
- setStatus(status);
- setKey(wordlistId);
- }
-
- public void setStatus(final int status) {
- if (status == mStatus) return;
- mStatus = status;
- setSummary(getSummary(status));
- }
-
- public boolean hasStatus(final int status) {
- return status == mStatus;
- }
-
- @Override
- public View onCreateView(final ViewGroup parent) {
- final View orphanedView = mInterfaceState.findFirstOrphanedView();
- if (null != orphanedView) return orphanedView; // Will be sent to onBindView
- final View newView = super.onCreateView(parent);
- return mInterfaceState.addToCacheAndReturnView(newView);
- }
-
- public boolean hasPriorityOver(final int otherPrefStatus) {
- // Both of these should be one of MetadataDbHelper.STATUS_*
- return mStatus > otherPrefStatus;
- }
-
- private String getSummary(final int status) {
- final Context context = getContext();
- switch (status) {
- // If we are deleting the word list, for the user it's like it's already deleted.
- // It should be reinstallable. Exposing to the user the whole complexity of
- // the delayed deletion process between the dictionary pack and Android Keyboard
- // would only be confusing.
- case MetadataDbHelper.STATUS_DELETING:
- case MetadataDbHelper.STATUS_AVAILABLE:
- return context.getString(R.string.dictionary_available);
- case MetadataDbHelper.STATUS_DOWNLOADING:
- return context.getString(R.string.dictionary_downloading);
- case MetadataDbHelper.STATUS_INSTALLED:
- return context.getString(R.string.dictionary_installed);
- case MetadataDbHelper.STATUS_DISABLED:
- return context.getString(R.string.dictionary_disabled);
- default:
- return NO_STATUS_MESSAGE;
- }
- }
-
- // The table below needs to be kept in sync with MetadataDbHelper.STATUS_* since it uses
- // the values as indices.
- private static final int sStatusActionList[][] = {
- // MetadataDbHelper.STATUS_UNKNOWN
- {},
- // MetadataDbHelper.STATUS_AVAILABLE
- { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT },
- // MetadataDbHelper.STATUS_DOWNLOADING
- { ButtonSwitcher.STATUS_CANCEL, ACTION_DISABLE_DICT },
- // MetadataDbHelper.STATUS_INSTALLED
- { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT },
- // MetadataDbHelper.STATUS_DISABLED
- { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT },
- // MetadataDbHelper.STATUS_DELETING
- // We show 'install' because the file is supposed to be deleted.
- // The user may reinstall it.
- { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT }
- };
-
- static int getButtonSwitcherStatus(final int status) {
- if (status >= sStatusActionList.length) {
- Log.e(TAG, "Unknown status " + status);
- return ButtonSwitcher.STATUS_NO_BUTTON;
- }
- return sStatusActionList[status][0];
- }
-
- static int getActionIdFromStatusAndMenuEntry(final int status) {
- if (status >= sStatusActionList.length) {
- Log.e(TAG, "Unknown status " + status);
- return ACTION_UNKNOWN;
- }
- return sStatusActionList[status][1];
- }
-
- private void disableDict() {
- final Context context = getContext();
- final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
- CommonPreferences.disable(prefs, mWordlistId);
- UpdateHandler.markAsUnused(context, mClientId, mWordlistId, mVersion, mStatus);
- if (MetadataDbHelper.STATUS_DOWNLOADING == mStatus) {
- setStatus(MetadataDbHelper.STATUS_AVAILABLE);
- } else if (MetadataDbHelper.STATUS_INSTALLED == mStatus) {
- // Interface-wise, we should no longer be able to come here. However, this is still
- // the right thing to do if we do come here.
- setStatus(MetadataDbHelper.STATUS_DISABLED);
- } else {
- Log.e(TAG, "Unexpected state of the word list for disabling " + mStatus);
- }
- }
-
- private void enableDict() {
- final Context context = getContext();
- final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
- CommonPreferences.enable(prefs, mWordlistId);
- // Explicit enabling by the user : allow downloading on metered data connection.
- UpdateHandler.markAsUsed(context, mClientId, mWordlistId, mVersion, mStatus, true);
- if (MetadataDbHelper.STATUS_AVAILABLE == mStatus) {
- setStatus(MetadataDbHelper.STATUS_DOWNLOADING);
- } else if (MetadataDbHelper.STATUS_DISABLED == mStatus
- || MetadataDbHelper.STATUS_DELETING == mStatus) {
- // If the status is DELETING, it means Android Keyboard
- // has not deleted the word list yet, so we can safely
- // turn it to 'installed'. The status DISABLED is still supported internally to
- // avoid breaking older installations and all but there should not be a way to
- // disable a word list through the interface any more.
- setStatus(MetadataDbHelper.STATUS_INSTALLED);
- } else {
- Log.e(TAG, "Unexpected state of the word list for enabling " + mStatus);
- }
- }
-
- private void deleteDict() {
- final Context context = getContext();
- final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
- CommonPreferences.disable(prefs, mWordlistId);
- setStatus(MetadataDbHelper.STATUS_DELETING);
- UpdateHandler.markAsDeleting(context, mClientId, mWordlistId, mVersion, mStatus);
- }
-
- @Override
- protected void onBindView(final View view) {
- super.onBindView(view);
- ((ViewGroup)view).setLayoutTransition(null);
-
- final DictionaryDownloadProgressBar progressBar =
- (DictionaryDownloadProgressBar)view.findViewById(R.id.dictionary_line_progress_bar);
- final TextView status = (TextView)view.findViewById(android.R.id.summary);
- progressBar.setIds(mClientId, mWordlistId);
- progressBar.setMax(mFilesize);
- final boolean showProgressBar = (MetadataDbHelper.STATUS_DOWNLOADING == mStatus);
- setSummary(getSummary(mStatus));
- status.setVisibility(showProgressBar ? View.INVISIBLE : View.VISIBLE);
- progressBar.setVisibility(showProgressBar ? View.VISIBLE : View.INVISIBLE);
-
- final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)view.findViewById(
- R.id.wordlist_button_switcher);
- // We need to clear the state of the button switcher, because we reuse views; if we didn't
- // reset it would animate from whatever its old state was.
- buttonSwitcher.reset(mInterfaceState);
- if (mInterfaceState.isOpen(mWordlistId)) {
- // The button is open.
- final int previousStatus = mInterfaceState.getStatus(mWordlistId);
- buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(previousStatus));
- if (previousStatus != mStatus) {
- // We come here if the status has changed since last time. We need to animate
- // the transition.
- buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus));
- mInterfaceState.setOpen(mWordlistId, mStatus);
- }
- } else {
- // The button is closed.
- buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON);
- }
- buttonSwitcher.setInternalOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(final View v) {
- onActionButtonClicked();
- }
- });
- view.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(final View v) {
- onWordListClicked(v);
- }
- });
- }
-
- void onWordListClicked(final View v) {
- // Note : v is the preference view
- final ViewParent parent = v.getParent();
- // Just in case something changed in the framework, test for the concrete class
- if (!(parent instanceof ListView)) return;
- final ListView listView = (ListView)parent;
- final int indexToOpen;
- // Close all first, we'll open back any item that needs to be open.
- final boolean wasOpen = mInterfaceState.isOpen(mWordlistId);
- mInterfaceState.closeAll();
- if (wasOpen) {
- // This button being shown. Take note that we don't want to open any button in the
- // loop below.
- indexToOpen = -1;
- } else {
- // This button was not being shown. Open it, and remember the index of this
- // child as the one to open in the following loop.
- mInterfaceState.setOpen(mWordlistId, mStatus);
- indexToOpen = listView.indexOfChild(v);
- }
- final int lastDisplayedIndex =
- listView.getLastVisiblePosition() - listView.getFirstVisiblePosition();
- // The "lastDisplayedIndex" is actually displayed, hence the <=
- for (int i = 0; i <= lastDisplayedIndex; ++i) {
- final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i)
- .findViewById(R.id.wordlist_button_switcher);
- if (i == indexToOpen) {
- buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus));
- } else {
- buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON);
- }
- }
- }
-
- void onActionButtonClicked() {
- switch (getActionIdFromStatusAndMenuEntry(mStatus)) {
- case ACTION_ENABLE_DICT:
- enableDict();
- break;
- case ACTION_DISABLE_DICT:
- disableDict();
- break;
- case ACTION_DELETE_DICT:
- deleteDict();
- break;
- default:
- Log.e(TAG, "Unknown menu item pressed");
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java
deleted file mode 100644
index fee93f0c6..000000000
--- a/java/src/com/android/inputmethod/event/Combiner.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nonnull;
-
-/**
- * A generic interface for combiners. Combiners are objects that transform chains of input events
- * into committable strings and manage feedback to show to the user on the combining state.
- */
-public interface Combiner {
- /**
- * Process an event, possibly combining it with the existing state and return the new event.
- *
- * If this event does not result in any new event getting passed down the chain, this method
- * returns null. It may also modify the previous event list if appropriate.
- *
- * @param previousEvents the previous events in this composition.
- * @param event the event to combine with the existing state.
- * @return the resulting event.
- */
- @Nonnull
- Event processEvent(ArrayList<Event> previousEvents, Event event);
-
- /**
- * Get the feedback that should be shown to the user for the current state of this combiner.
- * @return A CharSequence representing the feedback to show users. It may include styles.
- */
- CharSequence getCombiningStateFeedback();
-
- /**
- * Reset the state of this combiner, for example when the cursor was moved.
- */
- void reset();
-}
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
deleted file mode 100644
index d77ece8e6..000000000
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-
-import com.android.inputmethod.latin.common.Constants;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nonnull;
-
-/**
- * This class implements the logic chain between receiving events and generating code points.
- *
- * Event sources are multiple. It may be a hardware keyboard, a D-PAD, a software keyboard,
- * or any exotic input source.
- * This class will orchestrate the composing chain that starts with an event as its input. Each
- * composer will be given turns one after the other.
- * The output is composed of two sequences of code points: the first, representing the already
- * finished combining part, will be shown normally as the composing string, while the second is
- * feedback on the composing state and will typically be shown with different styling such as
- * a colored background.
- */
-public class CombinerChain {
- // The already combined text, as described above
- private StringBuilder mCombinedText;
- // The feedback on the composing state, as described above
- private SpannableStringBuilder mStateFeedback;
- private final ArrayList<Combiner> mCombiners;
-
- /**
- * Create an combiner chain.
- *
- * The combiner chain takes events as inputs and outputs code points and combining state.
- * For example, if the input language is Japanese, the combining chain will typically perform
- * kana conversion. This takes a string for initial text, taken to be present before the
- * cursor: we'll start after this.
- *
- * @param initialText The text that has already been combined so far.
- */
- public CombinerChain(final String initialText) {
- mCombiners = new ArrayList<>();
- // The dead key combiner is always active, and always first
- mCombiners.add(new DeadKeyCombiner());
- mCombinedText = new StringBuilder(initialText);
- mStateFeedback = new SpannableStringBuilder();
- }
-
- public void reset() {
- mCombinedText.setLength(0);
- mStateFeedback.clear();
- for (final Combiner c : mCombiners) {
- c.reset();
- }
- }
-
- private void updateStateFeedback() {
- mStateFeedback.clear();
- for (int i = mCombiners.size() - 1; i >= 0; --i) {
- mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback());
- }
- }
-
- /**
- * Process an event through the combining chain, and return a processed event to apply.
- * @param previousEvents the list of previous events in this composition
- * @param newEvent the new event to process
- * @return the processed event. It may be the same event, or a consumed event, or a completely
- * new event. However it may never be null.
- */
- @Nonnull
- public Event processEvent(final ArrayList<Event> previousEvents,
- @Nonnull final Event newEvent) {
- final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents);
- Event event = newEvent;
- for (final Combiner combiner : mCombiners) {
- // A combiner can never return more than one event; it can return several
- // code points, but they should be encapsulated within one event.
- event = combiner.processEvent(modifiablePreviousEvents, event);
- if (event.isConsumed()) {
- // If the event is consumed, then we don't pass it to subsequent combiners:
- // they should not see it at all.
- break;
- }
- }
- updateStateFeedback();
- return event;
- }
-
- /**
- * Apply a processed event.
- * @param event the event to be applied
- */
- public void applyProcessedEvent(final Event event) {
- if (null != event) {
- // TODO: figure out the generic way of doing this
- if (Constants.CODE_DELETE == event.mKeyCode) {
- final int length = mCombinedText.length();
- if (length > 0) {
- final int lastCodePoint = mCombinedText.codePointBefore(length);
- mCombinedText.delete(length - Character.charCount(lastCodePoint), length);
- }
- } else {
- final CharSequence textToCommit = event.getTextToCommit();
- if (!TextUtils.isEmpty(textToCommit)) {
- mCombinedText.append(textToCommit);
- }
- }
- }
- updateStateFeedback();
- }
-
- /**
- * Get the char sequence that should be displayed as the composing word. It may include
- * styling spans.
- */
- public CharSequence getComposingWordWithCombiningFeedback() {
- final SpannableStringBuilder s = new SpannableStringBuilder(mCombinedText);
- return s.append(mStateFeedback);
- }
-}
diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
deleted file mode 100644
index 1a28bb1d5..000000000
--- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import android.text.TextUtils;
-import android.util.SparseIntArray;
-
-import com.android.inputmethod.latin.common.Constants;
-
-import java.text.Normalizer;
-import java.util.ArrayList;
-
-import javax.annotation.Nonnull;
-
-/**
- * A combiner that handles dead keys.
- */
-public class DeadKeyCombiner implements Combiner {
-
- private static class Data {
- // This class data taken from KeyCharacterMap.java.
-
- /* Characters used to display placeholders for dead keys. */
- private static final int ACCENT_ACUTE = '\u00B4';
- private static final int ACCENT_BREVE = '\u02D8';
- private static final int ACCENT_CARON = '\u02C7';
- private static final int ACCENT_CEDILLA = '\u00B8';
- private static final int ACCENT_CIRCUMFLEX = '\u02C6';
- private static final int ACCENT_COMMA_ABOVE = '\u1FBD';
- private static final int ACCENT_COMMA_ABOVE_RIGHT = '\u02BC';
- private static final int ACCENT_DOT_ABOVE = '\u02D9';
- private static final int ACCENT_DOT_BELOW = Constants.CODE_PERIOD; // approximate
- private static final int ACCENT_DOUBLE_ACUTE = '\u02DD';
- private static final int ACCENT_GRAVE = '\u02CB';
- private static final int ACCENT_HOOK_ABOVE = '\u02C0';
- private static final int ACCENT_HORN = Constants.CODE_SINGLE_QUOTE; // approximate
- private static final int ACCENT_MACRON = '\u00AF';
- private static final int ACCENT_MACRON_BELOW = '\u02CD';
- private static final int ACCENT_OGONEK = '\u02DB';
- private static final int ACCENT_REVERSED_COMMA_ABOVE = '\u02BD';
- private static final int ACCENT_RING_ABOVE = '\u02DA';
- private static final int ACCENT_STROKE = Constants.CODE_DASH; // approximate
- private static final int ACCENT_TILDE = '\u02DC';
- private static final int ACCENT_TURNED_COMMA_ABOVE = '\u02BB';
- private static final int ACCENT_UMLAUT = '\u00A8';
- private static final int ACCENT_VERTICAL_LINE_ABOVE = '\u02C8';
- private static final int ACCENT_VERTICAL_LINE_BELOW = '\u02CC';
-
- /* Legacy dead key display characters used in previous versions of the API (before L)
- * We still support these characters by mapping them to their non-legacy version. */
- private static final int ACCENT_GRAVE_LEGACY = Constants.CODE_GRAVE_ACCENT;
- private static final int ACCENT_CIRCUMFLEX_LEGACY = Constants.CODE_CIRCUMFLEX_ACCENT;
- private static final int ACCENT_TILDE_LEGACY = Constants.CODE_TILDE;
-
- /**
- * Maps Unicode combining diacritical to display-form dead key.
- */
- static final SparseIntArray sCombiningToAccent = new SparseIntArray();
- static final SparseIntArray sAccentToCombining = new SparseIntArray();
- static {
- // U+0300: COMBINING GRAVE ACCENT
- addCombining('\u0300', ACCENT_GRAVE);
- // U+0301: COMBINING ACUTE ACCENT
- addCombining('\u0301', ACCENT_ACUTE);
- // U+0302: COMBINING CIRCUMFLEX ACCENT
- addCombining('\u0302', ACCENT_CIRCUMFLEX);
- // U+0303: COMBINING TILDE
- addCombining('\u0303', ACCENT_TILDE);
- // U+0304: COMBINING MACRON
- addCombining('\u0304', ACCENT_MACRON);
- // U+0306: COMBINING BREVE
- addCombining('\u0306', ACCENT_BREVE);
- // U+0307: COMBINING DOT ABOVE
- addCombining('\u0307', ACCENT_DOT_ABOVE);
- // U+0308: COMBINING DIAERESIS
- addCombining('\u0308', ACCENT_UMLAUT);
- // U+0309: COMBINING HOOK ABOVE
- addCombining('\u0309', ACCENT_HOOK_ABOVE);
- // U+030A: COMBINING RING ABOVE
- addCombining('\u030A', ACCENT_RING_ABOVE);
- // U+030B: COMBINING DOUBLE ACUTE ACCENT
- addCombining('\u030B', ACCENT_DOUBLE_ACUTE);
- // U+030C: COMBINING CARON
- addCombining('\u030C', ACCENT_CARON);
- // U+030D: COMBINING VERTICAL LINE ABOVE
- addCombining('\u030D', ACCENT_VERTICAL_LINE_ABOVE);
- // U+030E: COMBINING DOUBLE VERTICAL LINE ABOVE
- //addCombining('\u030E', ACCENT_DOUBLE_VERTICAL_LINE_ABOVE);
- // U+030F: COMBINING DOUBLE GRAVE ACCENT
- //addCombining('\u030F', ACCENT_DOUBLE_GRAVE);
- // U+0310: COMBINING CANDRABINDU
- //addCombining('\u0310', ACCENT_CANDRABINDU);
- // U+0311: COMBINING INVERTED BREVE
- //addCombining('\u0311', ACCENT_INVERTED_BREVE);
- // U+0312: COMBINING TURNED COMMA ABOVE
- addCombining('\u0312', ACCENT_TURNED_COMMA_ABOVE);
- // U+0313: COMBINING COMMA ABOVE
- addCombining('\u0313', ACCENT_COMMA_ABOVE);
- // U+0314: COMBINING REVERSED COMMA ABOVE
- addCombining('\u0314', ACCENT_REVERSED_COMMA_ABOVE);
- // U+0315: COMBINING COMMA ABOVE RIGHT
- addCombining('\u0315', ACCENT_COMMA_ABOVE_RIGHT);
- // U+031B: COMBINING HORN
- addCombining('\u031B', ACCENT_HORN);
- // U+0323: COMBINING DOT BELOW
- addCombining('\u0323', ACCENT_DOT_BELOW);
- // U+0326: COMBINING COMMA BELOW
- //addCombining('\u0326', ACCENT_COMMA_BELOW);
- // U+0327: COMBINING CEDILLA
- addCombining('\u0327', ACCENT_CEDILLA);
- // U+0328: COMBINING OGONEK
- addCombining('\u0328', ACCENT_OGONEK);
- // U+0329: COMBINING VERTICAL LINE BELOW
- addCombining('\u0329', ACCENT_VERTICAL_LINE_BELOW);
- // U+0331: COMBINING MACRON BELOW
- addCombining('\u0331', ACCENT_MACRON_BELOW);
- // U+0335: COMBINING SHORT STROKE OVERLAY
- addCombining('\u0335', ACCENT_STROKE);
- // U+0342: COMBINING GREEK PERISPOMENI
- //addCombining('\u0342', ACCENT_PERISPOMENI);
- // U+0344: COMBINING GREEK DIALYTIKA TONOS
- //addCombining('\u0344', ACCENT_DIALYTIKA_TONOS);
- // U+0345: COMBINING GREEK YPOGEGRAMMENI
- //addCombining('\u0345', ACCENT_YPOGEGRAMMENI);
-
- // One-way mappings to equivalent preferred accents.
- // U+0340: COMBINING GRAVE TONE MARK
- sCombiningToAccent.append('\u0340', ACCENT_GRAVE);
- // U+0341: COMBINING ACUTE TONE MARK
- sCombiningToAccent.append('\u0341', ACCENT_ACUTE);
- // U+0343: COMBINING GREEK KORONIS
- sCombiningToAccent.append('\u0343', ACCENT_COMMA_ABOVE);
-
- // One-way legacy mappings to preserve compatibility with older applications.
- // U+0300: COMBINING GRAVE ACCENT
- sAccentToCombining.append(ACCENT_GRAVE_LEGACY, '\u0300');
- // U+0302: COMBINING CIRCUMFLEX ACCENT
- sAccentToCombining.append(ACCENT_CIRCUMFLEX_LEGACY, '\u0302');
- // U+0303: COMBINING TILDE
- sAccentToCombining.append(ACCENT_TILDE_LEGACY, '\u0303');
- }
-
- private static void addCombining(int combining, int accent) {
- sCombiningToAccent.append(combining, accent);
- sAccentToCombining.append(accent, combining);
- }
-
- // Caution! This may only contain chars, not supplementary code points. It's unlikely
- // it will ever need to, but if it does we'll have to change this
- private static final SparseIntArray sNonstandardDeadCombinations = new SparseIntArray();
- static {
- // Non-standard decompositions.
- // Stroke modifier for Finnish multilingual keyboard and others.
- // U+0110: LATIN CAPITAL LETTER D WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'D', '\u0110');
- // U+01E4: LATIN CAPITAL LETTER G WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'G', '\u01e4');
- // U+0126: LATIN CAPITAL LETTER H WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'H', '\u0126');
- // U+0197: LATIN CAPITAL LETTER I WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'I', '\u0197');
- // U+0141: LATIN CAPITAL LETTER L WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'L', '\u0141');
- // U+00D8: LATIN CAPITAL LETTER O WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'O', '\u00d8');
- // U+0166: LATIN CAPITAL LETTER T WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'T', '\u0166');
- // U+0111: LATIN SMALL LETTER D WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'd', '\u0111');
- // U+01E5: LATIN SMALL LETTER G WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'g', '\u01e5');
- // U+0127: LATIN SMALL LETTER H WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'h', '\u0127');
- // U+0268: LATIN SMALL LETTER I WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'i', '\u0268');
- // U+0142: LATIN SMALL LETTER L WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'l', '\u0142');
- // U+00F8: LATIN SMALL LETTER O WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 'o', '\u00f8');
- // U+0167: LATIN SMALL LETTER T WITH STROKE
- addNonStandardDeadCombination(ACCENT_STROKE, 't', '\u0167');
- }
-
- private static void addNonStandardDeadCombination(final int deadCodePoint,
- final int spacingCodePoint, final int result) {
- final int combination = (deadCodePoint << 16) | spacingCodePoint;
- sNonstandardDeadCombinations.put(combination, result);
- }
-
- public static final int NOT_A_CHAR = 0;
- public static final int BITS_TO_SHIFT_DEAD_CODE_POINT_FOR_NON_STANDARD_COMBINATION = 16;
- // Get a non-standard combination
- public static char getNonstandardCombination(final int deadCodePoint,
- final int spacingCodePoint) {
- final int combination = spacingCodePoint |
- (deadCodePoint << BITS_TO_SHIFT_DEAD_CODE_POINT_FOR_NON_STANDARD_COMBINATION);
- return (char)sNonstandardDeadCombinations.get(combination, NOT_A_CHAR);
- }
- }
-
- // TODO: make this a list of events instead
- final StringBuilder mDeadSequence = new StringBuilder();
-
- @Nonnull
- private static Event createEventChainFromSequence(final @Nonnull CharSequence text,
- @Nonnull final Event originalEvent) {
- int index = text.length();
- if (index <= 0) {
- return originalEvent;
- }
- Event lastEvent = null;
- do {
- final int codePoint = Character.codePointBefore(text, index);
- lastEvent = Event.createHardwareKeypressEvent(codePoint,
- originalEvent.mKeyCode, lastEvent, false /* isKeyRepeat */);
- index -= Character.charCount(codePoint);
- } while (index > 0);
- return lastEvent;
- }
-
- @Override
- @Nonnull
- public Event processEvent(final ArrayList<Event> previousEvents, final Event event) {
- if (TextUtils.isEmpty(mDeadSequence)) {
- // No dead char is currently being tracked: this is the most common case.
- if (event.isDead()) {
- // The event was a dead key. Start tracking it.
- mDeadSequence.appendCodePoint(event.mCodePoint);
- return Event.createConsumedEvent(event);
- }
- // Regular keystroke when not keeping track of a dead key. Simply said, there are
- // no dead keys at all in the current input, so this combiner has nothing to do and
- // simply returns the event as is. The majority of events will go through this path.
- return event;
- }
- if (Character.isWhitespace(event.mCodePoint)
- || event.mCodePoint == mDeadSequence.codePointBefore(mDeadSequence.length())) {
- // When whitespace or twice the same dead key, we should output the dead sequence as is.
- final Event resultEvent = createEventChainFromSequence(mDeadSequence.toString(),
- event);
- mDeadSequence.setLength(0);
- return resultEvent;
- }
- if (event.isFunctionalKeyEvent()) {
- if (Constants.CODE_DELETE == event.mKeyCode) {
- // Remove the last code point
- final int trimIndex = mDeadSequence.length() - Character.charCount(
- mDeadSequence.codePointBefore(mDeadSequence.length()));
- mDeadSequence.setLength(trimIndex);
- return Event.createConsumedEvent(event);
- }
- return event;
- }
- if (event.isDead()) {
- mDeadSequence.appendCodePoint(event.mCodePoint);
- return Event.createConsumedEvent(event);
- }
- // Combine normally.
- final StringBuilder sb = new StringBuilder();
- sb.appendCodePoint(event.mCodePoint);
- int codePointIndex = 0;
- while (codePointIndex < mDeadSequence.length()) {
- final int deadCodePoint = mDeadSequence.codePointAt(codePointIndex);
- final char replacementSpacingChar =
- Data.getNonstandardCombination(deadCodePoint, event.mCodePoint);
- if (Data.NOT_A_CHAR != replacementSpacingChar) {
- sb.setCharAt(0, replacementSpacingChar);
- } else {
- final int combining = Data.sAccentToCombining.get(deadCodePoint);
- sb.appendCodePoint(0 == combining ? deadCodePoint : combining);
- }
- codePointIndex += Character.isSupplementaryCodePoint(deadCodePoint) ? 2 : 1;
- }
- final String normalizedString = Normalizer.normalize(sb, Normalizer.Form.NFC);
- final Event resultEvent = createEventChainFromSequence(normalizedString, event);
- mDeadSequence.setLength(0);
- return resultEvent;
- }
-
- @Override
- public void reset() {
- mDeadSequence.setLength(0);
- }
-
- @Override
- public CharSequence getCombiningStateFeedback() {
- return mDeadSequence;
- }
-}
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
deleted file mode 100644
index 58d878fbe..000000000
--- a/java/src/com/android/inputmethod/event/Event.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import javax.annotation.Nonnull;
-
-/**
- * Class representing a generic input event as handled by Latin IME.
- *
- * This contains information about the origin of the event, but it is generalized and should
- * represent a software keypress, hardware keypress, or d-pad move alike.
- * Very importantly, this does not necessarily result in inputting one character, or even anything
- * at all - it may be a dead key, it may be a partial input, it may be a special key on the
- * keyboard, it may be a cancellation of a keypress (e.g. in a soft keyboard the finger of the
- * user has slid out of the key), etc. It may also be a batch input from a gesture or handwriting
- * for example.
- * The combiner should figure out what to do with this.
- */
-public class Event {
- // Should the types below be represented by separate classes instead? It would be cleaner
- // but probably a bit too much
- // An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard.
- final public static int EVENT_TYPE_NOT_HANDLED = 0;
- // A key press that is part of input, for example pressing an alphabetic character on a
- // hardware qwerty keyboard. It may be part of a sequence that will be re-interpreted later
- // through combination.
- final public static int EVENT_TYPE_INPUT_KEYPRESS = 1;
- // A toggle event is triggered by a key that affects the previous character. An example would
- // be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with
- // repeated presses.
- final public static int EVENT_TYPE_TOGGLE = 2;
- // A mode event instructs the combiner to change modes. The canonical example would be the
- // hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard
- // if handled at the combiner level.
- final public static int EVENT_TYPE_MODE_KEY = 3;
- // An event corresponding to a gesture.
- final public static int EVENT_TYPE_GESTURE = 4;
- // An event corresponding to the manual pick of a suggestion.
- final public static int EVENT_TYPE_SUGGESTION_PICKED = 5;
- // An event corresponding to a string generated by some software process.
- final public static int EVENT_TYPE_SOFTWARE_GENERATED_STRING = 6;
- // An event corresponding to a cursor move
- final public static int EVENT_TYPE_CURSOR_MOVE = 7;
-
- // 0 is a valid code point, so we use -1 here.
- final public static int NOT_A_CODE_POINT = -1;
- // -1 is a valid key code, so we use 0 here.
- final public static int NOT_A_KEY_CODE = 0;
-
- final private static int FLAG_NONE = 0;
- // This event is a dead character, usually input by a dead key. Examples include dead-acute
- // or dead-abovering.
- final private static int FLAG_DEAD = 0x1;
- // This event is coming from a key repeat, software or hardware.
- final private static int FLAG_REPEAT = 0x2;
- // This event has already been consumed.
- final private static int FLAG_CONSUMED = 0x4;
-
- final private int mEventType; // The type of event - one of the constants above
- // The code point associated with the event, if relevant. This is a unicode code point, and
- // has nothing to do with other representations of the key. It is only relevant if this event
- // is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point
- // associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when
- // it's not relevant.
- final public int mCodePoint;
-
- // If applicable, this contains the string that should be input.
- final public CharSequence mText;
-
- // The key code associated with the event, if relevant. This is relevant whenever this event
- // has been triggered by a key press, but not for a gesture for example. This has conceptually
- // no link to the code point, although keys that enter a straight code point may often set
- // this to be equal to mCodePoint for convenience. If this is not a key, this must contain
- // NOT_A_KEY_CODE.
- final public int mKeyCode;
-
- // Coordinates of the touch event, if relevant. If useful, we may want to replace this with
- // a MotionEvent or something in the future. This is only relevant when the keypress is from
- // a software keyboard obviously, unless there are touch-sensitive hardware keyboards in the
- // future or some other awesome sauce.
- final public int mX;
- final public int mY;
-
- // Some flags that can't go into the key code. It's a bit field of FLAG_*
- final private int mFlags;
-
- // If this is of type EVENT_TYPE_SUGGESTION_PICKED, this must not be null (and must be null in
- // other cases).
- final public SuggestedWordInfo mSuggestedWordInfo;
-
- // The next event, if any. Null if there is no next event yet.
- final public Event mNextEvent;
-
- // This method is private - to create a new event, use one of the create* utility methods.
- private Event(final int type, final CharSequence text, final int codePoint, final int keyCode,
- final int x, final int y, final SuggestedWordInfo suggestedWordInfo, final int flags,
- final Event next) {
- mEventType = type;
- mText = text;
- mCodePoint = codePoint;
- mKeyCode = keyCode;
- mX = x;
- mY = y;
- mSuggestedWordInfo = suggestedWordInfo;
- mFlags = flags;
- mNextEvent = next;
- // Validity checks
- // mSuggestedWordInfo is non-null if and only if the type is SUGGESTION_PICKED
- if (EVENT_TYPE_SUGGESTION_PICKED == mEventType) {
- if (null == mSuggestedWordInfo) {
- throw new RuntimeException("Wrong event: SUGGESTION_PICKED event must have a "
- + "non-null SuggestedWordInfo");
- }
- } else {
- if (null != mSuggestedWordInfo) {
- throw new RuntimeException("Wrong event: only SUGGESTION_PICKED events may have " +
- "a non-null SuggestedWordInfo");
- }
- }
- }
-
- @Nonnull
- public static Event createSoftwareKeypressEvent(final int codePoint, final int keyCode,
- final int x, final int y, final boolean isKeyRepeat) {
- return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, x, y,
- null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, null);
- }
-
- @Nonnull
- public static Event createHardwareKeypressEvent(final int codePoint, final int keyCode,
- final Event next, final boolean isKeyRepeat) {
- return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode,
- Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
- null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, next);
- }
-
- // This creates an input event for a dead character. @see {@link #FLAG_DEAD}
- @ExternallyReferenced
- @Nonnull
- public static Event createDeadEvent(final int codePoint, final int keyCode, final Event next) {
- // TODO: add an argument or something if we ever create a software layout with dead keys.
- return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode,
- Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
- null /* suggestedWordInfo */, FLAG_DEAD, next);
- }
-
- /**
- * Create an input event with nothing but a code point. This is the most basic possible input
- * event; it contains no information on many things the IME requires to function correctly,
- * so avoid using it unless really nothing is known about this input.
- * @param codePoint the code point.
- * @return an event for this code point.
- */
- @Nonnull
- public static Event createEventForCodePointFromUnknownSource(final int codePoint) {
- // TODO: should we have a different type of event for this? After all, it's not a key press.
- return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
- null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
- }
-
- /**
- * Creates an input event with a code point and x, y coordinates. This is typically used when
- * resuming a previously-typed word, when the coordinates are still known.
- * @param codePoint the code point to input.
- * @param x the X coordinate.
- * @param y the Y coordinate.
- * @return an event for this code point and coordinates.
- */
- @Nonnull
- public static Event createEventForCodePointFromAlreadyTypedText(final int codePoint,
- final int x, final int y) {
- // TODO: should we have a different type of event for this? After all, it's not a key press.
- return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE,
- x, y, null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
- }
-
- /**
- * Creates an input event representing the manual pick of a suggestion.
- * @return an event for this suggestion pick.
- */
- @Nonnull
- public static Event createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo) {
- return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord,
- NOT_A_CODE_POINT, NOT_A_KEY_CODE,
- Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
- suggestedWordInfo, FLAG_NONE, null /* next */);
- }
-
- /**
- * Creates an input event with a CharSequence. This is used by some software processes whose
- * output is a string, possibly with styling. Examples include press on a multi-character key,
- * or combination that outputs a string.
- * @param text the CharSequence associated with this event.
- * @param keyCode the key code, or NOT_A_KEYCODE if not applicable.
- * @return an event for this text.
- */
- @Nonnull
- public static Event createSoftwareTextEvent(final CharSequence text, final int keyCode) {
- return new Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
- null /* suggestedWordInfo */, FLAG_NONE, null /* next */);
- }
-
- /**
- * Creates an input event representing the manual pick of a punctuation suggestion.
- * @return an event for this suggestion pick.
- */
- @Nonnull
- public static Event createPunctuationSuggestionPickedEvent(
- final SuggestedWordInfo suggestedWordInfo) {
- final int primaryCode = suggestedWordInfo.mWord.charAt(0);
- return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode,
- NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE,
- Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE,
- null /* next */);
- }
-
- /**
- * Creates an input event representing moving the cursor. The relative move amount is stored
- * in mX.
- * @param moveAmount the relative move amount.
- * @return an event for this cursor move.
- */
- @Nonnull
- public static Event createCursorMovedEvent(final int moveAmount) {
- return new Event(EVENT_TYPE_CURSOR_MOVE, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE,
- moveAmount, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null);
- }
-
- /**
- * Creates an event identical to the passed event, but that has already been consumed.
- * @param source the event to copy the properties of.
- * @return an identical event marked as consumed.
- */
- @Nonnull
- public static Event createConsumedEvent(final Event source) {
- // A consumed event should not input any text at all, so we pass the empty string as text.
- return new Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode,
- source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags | FLAG_CONSUMED,
- source.mNextEvent);
- }
-
- @Nonnull
- public static Event createNotHandledEvent() {
- return new Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
- null /* suggestedWordInfo */, FLAG_NONE, null);
- }
-
- // Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys
- // that result in input like letters or space.
- public boolean isFunctionalKeyEvent() {
- // This logic may need to be refined in the future
- return NOT_A_CODE_POINT == mCodePoint;
- }
-
- // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD}
- public boolean isDead() {
- return 0 != (FLAG_DEAD & mFlags);
- }
-
- public boolean isKeyRepeat() {
- return 0 != (FLAG_REPEAT & mFlags);
- }
-
- public boolean isConsumed() { return 0 != (FLAG_CONSUMED & mFlags); }
-
- public boolean isGesture() { return EVENT_TYPE_GESTURE == mEventType; }
-
- // Returns whether this is a fake key press from the suggestion strip. This happens with
- // punctuation signs selected from the suggestion strip.
- public boolean isSuggestionStripPress() {
- return EVENT_TYPE_SUGGESTION_PICKED == mEventType;
- }
-
- public boolean isHandled() {
- return EVENT_TYPE_NOT_HANDLED != mEventType;
- }
-
- public CharSequence getTextToCommit() {
- if (isConsumed()) {
- return ""; // A consumed event should input no text.
- }
- switch (mEventType) {
- case EVENT_TYPE_MODE_KEY:
- case EVENT_TYPE_NOT_HANDLED:
- case EVENT_TYPE_TOGGLE:
- case EVENT_TYPE_CURSOR_MOVE:
- return "";
- case EVENT_TYPE_INPUT_KEYPRESS:
- return StringUtils.newSingleCodePointString(mCodePoint);
- case EVENT_TYPE_GESTURE:
- case EVENT_TYPE_SOFTWARE_GENERATED_STRING:
- case EVENT_TYPE_SUGGESTION_PICKED:
- return mText;
- }
- throw new RuntimeException("Unknown event type: " + mEventType);
- }
-}
diff --git a/java/src/com/android/inputmethod/event/EventDecoder.java b/java/src/com/android/inputmethod/event/EventDecoder.java
deleted file mode 100644
index 7ff0166a3..000000000
--- a/java/src/com/android/inputmethod/event/EventDecoder.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-/**
- * A generic interface for event decoders.
- */
-public interface EventDecoder {
-
-}
diff --git a/java/src/com/android/inputmethod/event/HardwareEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareEventDecoder.java
deleted file mode 100644
index 6a6bd7bc5..000000000
--- a/java/src/com/android/inputmethod/event/HardwareEventDecoder.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import android.view.KeyEvent;
-
-/**
- * An event decoder for hardware events.
- */
-public interface HardwareEventDecoder extends EventDecoder {
- public Event decodeHardwareKey(final KeyEvent keyEvent);
-}
diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
deleted file mode 100644
index 3a4097d7f..000000000
--- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-
-import com.android.inputmethod.latin.common.Constants;
-
-/**
- * A hardware event decoder for a hardware qwerty-ish keyboard.
- *
- * The events are always hardware keypresses, but they can be key down or key up events, they
- * can be dead keys, they can be meta keys like shift or ctrl... This does not deal with
- * 10-key like keyboards; a different decoder is used for this.
- */
-public class HardwareKeyboardEventDecoder implements HardwareEventDecoder {
- final int mDeviceId;
-
- public HardwareKeyboardEventDecoder(final int deviceId) {
- mDeviceId = deviceId;
- // TODO: get the layout for this hardware keyboard
- }
-
- @Override
- public Event decodeHardwareKey(final KeyEvent keyEvent) {
- // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value
- // that includes both the unicode char in the lower 21 bits and flags in the upper bits,
- // hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info.
- final int codePointAndFlags = keyEvent.getUnicodeChar();
- // The keyCode is the abstraction used by the KeyEvent to represent different keys that
- // do not necessarily map to a unicode character. This represents a physical key, like
- // the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
- final int keyCode = keyEvent.getKeyCode();
- final boolean isKeyRepeat = (0 != keyEvent.getRepeatCount());
- if (KeyEvent.KEYCODE_DEL == keyCode) {
- return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, Constants.CODE_DELETE,
- null /* next */, isKeyRepeat);
- }
- if (keyEvent.isPrintingKey() || KeyEvent.KEYCODE_SPACE == keyCode
- || KeyEvent.KEYCODE_ENTER == keyCode) {
- if (0 != (codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT)) {
- // A dead key.
- return Event.createDeadEvent(
- codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode,
- null /* next */);
- }
- if (KeyEvent.KEYCODE_ENTER == keyCode) {
- // The Enter key. If the Shift key is not being pressed, this should send a
- // CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the
- // Shift key is being pressed, this should send a CODE_SHIFT_ENTER and let
- // Latin IME decide what to do with it.
- if (keyEvent.isShiftPressed()) {
- return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT,
- Constants.CODE_SHIFT_ENTER, null /* next */, isKeyRepeat);
- }
- return Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode,
- null /* next */, isKeyRepeat);
- }
- // If not Enter, then this is just a regular keypress event for a normal character
- // that can be committed right away, taking into account the current state.
- return Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */,
- isKeyRepeat);
- }
- return Event.createNotHandledEvent();
- }
-}
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
deleted file mode 100644
index 5bc9111de..000000000
--- a/java/src/com/android/inputmethod/event/InputTransaction.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-/**
- * An object encapsulating a single transaction for input.
- */
-public class InputTransaction {
- // UPDATE_LATER is stronger than UPDATE_NOW. The reason for this is, if we have to update later,
- // it's because something will change that we can't evaluate now, which means that even if we
- // re-evaluate now we'll have to do it again later. The only case where that wouldn't apply
- // would be if we needed to update now to find out the new state right away, but then we
- // can't do it with this deferred mechanism anyway.
- public static final int SHIFT_NO_UPDATE = 0;
- public static final int SHIFT_UPDATE_NOW = 1;
- public static final int SHIFT_UPDATE_LATER = 2;
-
- // Initial conditions
- public final SettingsValues mSettingsValues;
- public final Event mEvent;
- public final long mTimestamp;
- public final int mSpaceState;
- public final int mShiftState;
-
- // Outputs
- private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
- private boolean mRequiresUpdateSuggestions = false;
- private boolean mDidAffectContents = false;
- private boolean mDidAutoCorrect = false;
-
- public InputTransaction(final SettingsValues settingsValues, final Event event,
- final long timestamp, final int spaceState, final int shiftState) {
- mSettingsValues = settingsValues;
- mEvent = event;
- mTimestamp = timestamp;
- mSpaceState = spaceState;
- mShiftState = shiftState;
- }
-
- /**
- * Indicate that this transaction requires some type of shift update.
- * @param updateType What type of shift update this requires.
- */
- public void requireShiftUpdate(final int updateType) {
- mRequiredShiftUpdate = Math.max(mRequiredShiftUpdate, updateType);
- }
-
- /**
- * Gets what type of shift update this transaction requires.
- * @return The shift update type.
- */
- public int getRequiredShiftUpdate() {
- return mRequiredShiftUpdate;
- }
-
- /**
- * Indicate that this transaction requires updating the suggestions.
- */
- public void setRequiresUpdateSuggestions() {
- mRequiresUpdateSuggestions = true;
- }
-
- /**
- * Find out whether this transaction requires updating the suggestions.
- * @return Whether this transaction requires updating the suggestions.
- */
- public boolean requiresUpdateSuggestions() {
- return mRequiresUpdateSuggestions;
- }
-
- /**
- * Indicate that this transaction affected the contents of the editor.
- */
- public void setDidAffectContents() {
- mDidAffectContents = true;
- }
-
- /**
- * Find out whether this transaction affected contents of the editor.
- * @return Whether this transaction affected contents of the editor.
- */
- public boolean didAffectContents() {
- return mDidAffectContents;
- }
-
- /**
- * Indicate that this transaction performed an auto-correction.
- */
- public void setDidAutoCorrect() {
- mDidAutoCorrect = true;
- }
-
- /**
- * Find out whether this transaction performed an auto-correction.
- * @return Whether this transaction performed an auto-correction.
- */
- public boolean didAutoCorrect() {
- return mDidAutoCorrect;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
deleted file mode 100644
index 299d1b7c5..000000000
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ /dev/null
@@ -1,1022 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
-import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT;
-import static com.android.inputmethod.latin.common.Constants.CODE_SHIFT;
-import static com.android.inputmethod.latin.common.Constants.CODE_SWITCH_ALPHA_SYMBOL;
-import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED;
-
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-
-import com.android.inputmethod.keyboard.internal.KeyDrawParams;
-import com.android.inputmethod.keyboard.internal.KeySpecParser;
-import com.android.inputmethod.keyboard.internal.KeyStyle;
-import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
-import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
-import com.android.inputmethod.keyboard.internal.KeyboardRow;
-import com.android.inputmethod.keyboard.internal.MoreKeySpec;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Class for describing the position and characteristics of a single key in the keyboard.
- */
-public class Key implements Comparable<Key> {
- /**
- * The key code (unicode or custom code) that this key generates.
- */
- private final int mCode;
-
- /** Label to display */
- private final String mLabel;
- /** Hint label to display on the key in conjunction with the label */
- private final String mHintLabel;
- /** Flags of the label */
- private final int mLabelFlags;
- private static final int LABEL_FLAGS_ALIGN_HINT_LABEL_TO_BOTTOM = 0x02;
- private static final int LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM = 0x04;
- private static final int LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER = 0x08;
- // Font typeface specification.
- private static final int LABEL_FLAGS_FONT_MASK = 0x30;
- private static final int LABEL_FLAGS_FONT_NORMAL = 0x10;
- private static final int LABEL_FLAGS_FONT_MONO_SPACE = 0x20;
- private static final int LABEL_FLAGS_FONT_DEFAULT = 0x30;
- // Start of key text ratio enum values
- private static final int LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK = 0x1C0;
- private static final int LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO = 0x40;
- private static final int LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO = 0x80;
- private static final int LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO = 0xC0;
- private static final int LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO = 0x140;
- // End of key text ratio mask enum values
- private static final int LABEL_FLAGS_HAS_POPUP_HINT = 0x200;
- private static final int LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT = 0x400;
- private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800;
- // The bit to calculate the ratio of key label width against key width. If autoXScale bit is on
- // and autoYScale bit is off, the key label may be shrunk only for X-direction.
- // If both autoXScale and autoYScale bits are on, the key label text size may be auto scaled.
- private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000;
- private static final int LABEL_FLAGS_AUTO_Y_SCALE = 0x8000;
- private static final int LABEL_FLAGS_AUTO_SCALE = LABEL_FLAGS_AUTO_X_SCALE
- | LABEL_FLAGS_AUTO_Y_SCALE;
- private static final int LABEL_FLAGS_PRESERVE_CASE = 0x10000;
- private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x20000;
- private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x40000;
- private static final int LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR = 0x80000;
- private static final int LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO = 0x100000;
- private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000;
- private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000;
-
- /** Icon to display instead of a label. Icon takes precedence over a label */
- private final int mIconId;
-
- /** Width of the key, excluding the gap */
- private final int mWidth;
- /** Height of the key, excluding the gap */
- private final int mHeight;
- /**
- * The combined width in pixels of the horizontal gaps belonging to this key, both to the left
- * and to the right. I.e., mWidth + mHorizontalGap = total width belonging to the key.
- */
- private final int mHorizontalGap;
- /**
- * The combined height in pixels of the vertical gaps belonging to this key, both above and
- * below. I.e., mHeight + mVerticalGap = total height belonging to the key.
- */
- private final int mVerticalGap;
- /** X coordinate of the top-left corner of the key in the keyboard layout, excluding the gap. */
- private final int mX;
- /** Y coordinate of the top-left corner of the key in the keyboard layout, excluding the gap. */
- private final int mY;
- /** Hit bounding box of the key */
- @Nonnull
- private final Rect mHitBox = new Rect();
-
- /** More keys. It is guaranteed that this is null or an array of one or more elements */
- @Nullable
- private final MoreKeySpec[] mMoreKeys;
- /** More keys column number and flags */
- private final int mMoreKeysColumnAndFlags;
- private static final int MORE_KEYS_COLUMN_NUMBER_MASK = 0x000000ff;
- // If this flag is specified, more keys keyboard should have the specified number of columns.
- // Otherwise more keys keyboard should have less than or equal to the specified maximum number
- // of columns.
- private static final int MORE_KEYS_FLAGS_FIXED_COLUMN = 0x00000100;
- // If this flag is specified, the order of more keys is determined by the order in the more
- // keys' specification. Otherwise the order of more keys is automatically determined.
- private static final int MORE_KEYS_FLAGS_FIXED_ORDER = 0x00000200;
- private static final int MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER = 0;
- private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER =
- MORE_KEYS_FLAGS_FIXED_COLUMN;
- private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER =
- (MORE_KEYS_FLAGS_FIXED_COLUMN | MORE_KEYS_FLAGS_FIXED_ORDER);
- private static final int MORE_KEYS_FLAGS_HAS_LABELS = 0x40000000;
- private static final int MORE_KEYS_FLAGS_NEEDS_DIVIDERS = 0x20000000;
- private static final int MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY = 0x10000000;
- // TODO: Rename these specifiers to !autoOrder! and !fixedOrder! respectively.
- private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!";
- private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
- private static final String MORE_KEYS_HAS_LABELS = "!hasLabels!";
- private static final String MORE_KEYS_NEEDS_DIVIDERS = "!needsDividers!";
- private static final String MORE_KEYS_NO_PANEL_AUTO_MORE_KEY = "!noPanelAutoMoreKey!";
-
- /** Background type that represents different key background visual than normal one. */
- private final int mBackgroundType;
- public static final int BACKGROUND_TYPE_EMPTY = 0;
- public static final int BACKGROUND_TYPE_NORMAL = 1;
- public static final int BACKGROUND_TYPE_FUNCTIONAL = 2;
- public static final int BACKGROUND_TYPE_STICKY_OFF = 3;
- public static final int BACKGROUND_TYPE_STICKY_ON = 4;
- public static final int BACKGROUND_TYPE_ACTION = 5;
- public static final int BACKGROUND_TYPE_SPACEBAR = 6;
-
- private final int mActionFlags;
- private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01;
- private static final int ACTION_FLAGS_NO_KEY_PREVIEW = 0x02;
- private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04;
- private static final int ACTION_FLAGS_ENABLE_LONG_PRESS = 0x08;
-
- @Nullable
- private final KeyVisualAttributes mKeyVisualAttributes;
- @Nullable
- private final OptionalAttributes mOptionalAttributes;
-
- private static final class OptionalAttributes {
- /** Text to output when pressed. This can be multiple characters, like ".com" */
- public final String mOutputText;
- public final int mAltCode;
- /** Icon for disabled state */
- public final int mDisabledIconId;
- /** The visual insets */
- public final int mVisualInsetsLeft;
- public final int mVisualInsetsRight;
-
- private OptionalAttributes(final String outputText, final int altCode,
- final int disabledIconId, final int visualInsetsLeft, final int visualInsetsRight) {
- mOutputText = outputText;
- mAltCode = altCode;
- mDisabledIconId = disabledIconId;
- mVisualInsetsLeft = visualInsetsLeft;
- mVisualInsetsRight = visualInsetsRight;
- }
-
- @Nullable
- public static OptionalAttributes newInstance(final String outputText, final int altCode,
- final int disabledIconId, final int visualInsetsLeft, final int visualInsetsRight) {
- if (outputText == null && altCode == CODE_UNSPECIFIED
- && disabledIconId == ICON_UNDEFINED && visualInsetsLeft == 0
- && visualInsetsRight == 0) {
- return null;
- }
- return new OptionalAttributes(outputText, altCode, disabledIconId, visualInsetsLeft,
- visualInsetsRight);
- }
- }
-
- private final int mHashCode;
-
- /** The current pressed state of this key */
- private boolean mPressed;
- /** Key is enabled and responds on press */
- private boolean mEnabled = true;
-
- /**
- * Constructor for a key on <code>MoreKeyKeyboard</code>, on <code>MoreSuggestions</code>,
- * and in a <GridRows/>.
- */
- public Key(@Nullable final String label, final int iconId, final int code,
- @Nullable final String outputText, @Nullable final String hintLabel,
- final int labelFlags, final int backgroundType, final int x, final int y,
- final int width, final int height, final int horizontalGap, final int verticalGap) {
- mWidth = width - horizontalGap;
- mHeight = height - verticalGap;
- mHorizontalGap = horizontalGap;
- mVerticalGap = verticalGap;
- mHintLabel = hintLabel;
- mLabelFlags = labelFlags;
- mBackgroundType = backgroundType;
- // TODO: Pass keyActionFlags as an argument.
- mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
- mMoreKeys = null;
- mMoreKeysColumnAndFlags = 0;
- mLabel = label;
- mOptionalAttributes = OptionalAttributes.newInstance(outputText, CODE_UNSPECIFIED,
- ICON_UNDEFINED, 0 /* visualInsetsLeft */, 0 /* visualInsetsRight */);
- mCode = code;
- mEnabled = (code != CODE_UNSPECIFIED);
- mIconId = iconId;
- // Horizontal gap is divided equally to both sides of the key.
- mX = x + mHorizontalGap / 2;
- mY = y;
- mHitBox.set(x, y, x + width + 1, y + height);
- mKeyVisualAttributes = null;
-
- mHashCode = computeHashCode(this);
- }
-
- /**
- * Create a key with the given top-left coordinate and extract its attributes from a key
- * specification string, Key attribute array, key style, and etc.
- *
- * @param keySpec the key specification.
- * @param keyAttr the Key XML attributes array.
- * @param style the {@link KeyStyle} of this key.
- * @param params the keyboard building parameters.
- * @param row the row that this key belongs to. row's x-coordinate will be the right edge of
- * this key.
- */
- public Key(@Nullable final String keySpec, @Nonnull final TypedArray keyAttr,
- @Nonnull final KeyStyle style, @Nonnull final KeyboardParams params,
- @Nonnull final KeyboardRow row) {
- mHorizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
- mVerticalGap = params.mVerticalGap;
-
- final float horizontalGapFloat = mHorizontalGap;
- final int rowHeight = row.getRowHeight();
- mHeight = rowHeight - mVerticalGap;
-
- final float keyXPos = row.getKeyX(keyAttr);
- final float keyWidth = row.getKeyWidth(keyAttr, keyXPos);
- final int keyYPos = row.getKeyY();
-
- // Horizontal gap is divided equally to both sides of the key.
- mX = Math.round(keyXPos + horizontalGapFloat / 2);
- mY = keyYPos;
- mWidth = Math.round(keyWidth - horizontalGapFloat);
- mHitBox.set(Math.round(keyXPos), keyYPos, Math.round(keyXPos + keyWidth) + 1,
- keyYPos + rowHeight);
- // Update row to have current x coordinate.
- row.setXPos(keyXPos + keyWidth);
-
- mBackgroundType = style.getInt(keyAttr,
- R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
-
- final int baseWidth = params.mBaseWidth;
- final int visualInsetsLeft = Math.round(keyAttr.getFraction(
- R.styleable.Keyboard_Key_visualInsetsLeft, baseWidth, baseWidth, 0));
- final int visualInsetsRight = Math.round(keyAttr.getFraction(
- R.styleable.Keyboard_Key_visualInsetsRight, baseWidth, baseWidth, 0));
-
- mLabelFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
- | row.getDefaultKeyLabelFlags();
- final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId);
- final Locale localeForUpcasing = params.mId.getLocale();
- int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
- String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
-
- // Get maximum column order number and set a relevant mode value.
- int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
- | style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn,
- params.mMaxMoreKeysKeyboardColumn);
- int value;
- if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
- // Override with fixed column order number and set a relevant mode value.
- moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
- | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
- }
- if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
- // Override with fixed column order number and set a relevant mode value.
- moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
- | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
- }
- if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
- moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
- }
- if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
- moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
- }
- if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
- moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
- }
- mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
-
- final String[] additionalMoreKeys;
- if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
- additionalMoreKeys = null;
- } else {
- additionalMoreKeys = style.getStringArray(keyAttr,
- R.styleable.Keyboard_Key_additionalMoreKeys);
- }
- moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
- if (moreKeys != null) {
- actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
- mMoreKeys = new MoreKeySpec[moreKeys.length];
- for (int i = 0; i < moreKeys.length; i++) {
- mMoreKeys[i] = new MoreKeySpec(moreKeys[i], needsToUpcase, localeForUpcasing);
- }
- } else {
- mMoreKeys = null;
- }
- mActionFlags = actionFlags;
-
- mIconId = KeySpecParser.getIconId(keySpec);
- final int disabledIconId = KeySpecParser.getIconId(style.getString(keyAttr,
- R.styleable.Keyboard_Key_keyIconDisabled));
-
- final int code = KeySpecParser.getCode(keySpec);
- if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) {
- mLabel = params.mId.mCustomActionLabel;
- } else if (code >= Character.MIN_SUPPLEMENTARY_CODE_POINT) {
- // This is a workaround to have a key that has a supplementary code point in its label.
- // Because we can put a string in resource neither as a XML entity of a supplementary
- // code point nor as a surrogate pair.
- mLabel = new StringBuilder().appendCodePoint(code).toString();
- } else {
- final String label = KeySpecParser.getLabel(keySpec);
- mLabel = needsToUpcase
- ? StringUtils.toTitleCaseOfKeyLabel(label, localeForUpcasing)
- : label;
- }
- if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
- mHintLabel = null;
- } else {
- final String hintLabel = style.getString(
- keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
- mHintLabel = needsToUpcase
- ? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing)
- : hintLabel;
- }
- String outputText = KeySpecParser.getOutputText(keySpec);
- if (needsToUpcase) {
- outputText = StringUtils.toTitleCaseOfKeyLabel(outputText, localeForUpcasing);
- }
- // Choose the first letter of the label as primary code if not specified.
- if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
- && !TextUtils.isEmpty(mLabel)) {
- if (StringUtils.codePointCount(mLabel) == 1) {
- // Use the first letter of the hint label if shiftedLetterActivated flag is
- // specified.
- if (hasShiftedLetterHint() && isShiftedLetterActivated()) {
- mCode = mHintLabel.codePointAt(0);
- } else {
- mCode = mLabel.codePointAt(0);
- }
- } else {
- // In some locale and case, the character might be represented by multiple code
- // points, such as upper case Eszett of German alphabet.
- outputText = mLabel;
- mCode = CODE_OUTPUT_TEXT;
- }
- } else if (code == CODE_UNSPECIFIED && outputText != null) {
- if (StringUtils.codePointCount(outputText) == 1) {
- mCode = outputText.codePointAt(0);
- outputText = null;
- } else {
- mCode = CODE_OUTPUT_TEXT;
- }
- } else {
- mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing)
- : code;
- }
- final int altCodeInAttr = KeySpecParser.parseCode(
- style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
- final int altCode = needsToUpcase
- ? StringUtils.toTitleCaseOfKeyCode(altCodeInAttr, localeForUpcasing)
- : altCodeInAttr;
- mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
- disabledIconId, visualInsetsLeft, visualInsetsRight);
- mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
- mHashCode = computeHashCode(this);
- }
-
- /**
- * Copy constructor for DynamicGridKeyboard.GridKey.
- *
- * @param key the original key.
- */
- protected Key(@Nonnull final Key key) {
- this(key, key.mMoreKeys);
- }
-
- private Key(@Nonnull final Key key, @Nullable final MoreKeySpec[] moreKeys) {
- // Final attributes.
- mCode = key.mCode;
- mLabel = key.mLabel;
- mHintLabel = key.mHintLabel;
- mLabelFlags = key.mLabelFlags;
- mIconId = key.mIconId;
- mWidth = key.mWidth;
- mHeight = key.mHeight;
- mHorizontalGap = key.mHorizontalGap;
- mVerticalGap = key.mVerticalGap;
- mX = key.mX;
- mY = key.mY;
- mHitBox.set(key.mHitBox);
- mMoreKeys = moreKeys;
- mMoreKeysColumnAndFlags = key.mMoreKeysColumnAndFlags;
- mBackgroundType = key.mBackgroundType;
- mActionFlags = key.mActionFlags;
- mKeyVisualAttributes = key.mKeyVisualAttributes;
- mOptionalAttributes = key.mOptionalAttributes;
- mHashCode = key.mHashCode;
- // Key state.
- mPressed = key.mPressed;
- mEnabled = key.mEnabled;
- }
-
- @Nonnull
- public static Key removeRedundantMoreKeys(@Nonnull final Key key,
- @Nonnull final MoreKeySpec.LettersOnBaseLayout lettersOnBaseLayout) {
- final MoreKeySpec[] moreKeys = key.getMoreKeys();
- final MoreKeySpec[] filteredMoreKeys = MoreKeySpec.removeRedundantMoreKeys(
- moreKeys, lettersOnBaseLayout);
- return (filteredMoreKeys == moreKeys) ? key : new Key(key, filteredMoreKeys);
- }
-
- private static boolean needsToUpcase(final int labelFlags, final int keyboardElementId) {
- if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false;
- switch (keyboardElementId) {
- case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
- return true;
- default:
- return false;
- }
- }
-
- private static int computeHashCode(final Key key) {
- return Arrays.hashCode(new Object[] {
- key.mX,
- key.mY,
- key.mWidth,
- key.mHeight,
- key.mCode,
- key.mLabel,
- key.mHintLabel,
- key.mIconId,
- key.mBackgroundType,
- Arrays.hashCode(key.mMoreKeys),
- key.getOutputText(),
- key.mActionFlags,
- key.mLabelFlags,
- // Key can be distinguishable without the following members.
- // key.mOptionalAttributes.mAltCode,
- // key.mOptionalAttributes.mDisabledIconId,
- // key.mOptionalAttributes.mPreviewIconId,
- // key.mHorizontalGap,
- // key.mVerticalGap,
- // key.mOptionalAttributes.mVisualInsetLeft,
- // key.mOptionalAttributes.mVisualInsetRight,
- // key.mMaxMoreKeysColumn,
- });
- }
-
- private boolean equalsInternal(final Key o) {
- if (this == o) return true;
- return o.mX == mX
- && o.mY == mY
- && o.mWidth == mWidth
- && o.mHeight == mHeight
- && o.mCode == mCode
- && TextUtils.equals(o.mLabel, mLabel)
- && TextUtils.equals(o.mHintLabel, mHintLabel)
- && o.mIconId == mIconId
- && o.mBackgroundType == mBackgroundType
- && Arrays.equals(o.mMoreKeys, mMoreKeys)
- && TextUtils.equals(o.getOutputText(), getOutputText())
- && o.mActionFlags == mActionFlags
- && o.mLabelFlags == mLabelFlags;
- }
-
- @Override
- public int compareTo(Key o) {
- if (equalsInternal(o)) return 0;
- if (mHashCode > o.mHashCode) return 1;
- return -1;
- }
-
- @Override
- public int hashCode() {
- return mHashCode;
- }
-
- @Override
- public boolean equals(final Object o) {
- return o instanceof Key && equalsInternal((Key)o);
- }
-
- @Override
- public String toString() {
- return toShortString() + " " + getX() + "," + getY() + " " + getWidth() + "x" + getHeight();
- }
-
- public String toShortString() {
- final int code = getCode();
- if (code == Constants.CODE_OUTPUT_TEXT) {
- return getOutputText();
- }
- return Constants.printableCode(code);
- }
-
- public String toLongString() {
- final int iconId = getIconId();
- final String topVisual = (iconId == KeyboardIconsSet.ICON_UNDEFINED)
- ? KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(iconId) : getLabel();
- final String hintLabel = getHintLabel();
- final String visual = (hintLabel == null) ? topVisual : topVisual + "^" + hintLabel;
- return toString() + " " + visual + "/" + backgroundName(mBackgroundType);
- }
-
- private static String backgroundName(final int backgroundType) {
- switch (backgroundType) {
- case BACKGROUND_TYPE_EMPTY: return "empty";
- case BACKGROUND_TYPE_NORMAL: return "normal";
- case BACKGROUND_TYPE_FUNCTIONAL: return "functional";
- case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff";
- case BACKGROUND_TYPE_STICKY_ON: return "stickyOn";
- case BACKGROUND_TYPE_ACTION: return "action";
- case BACKGROUND_TYPE_SPACEBAR: return "spacebar";
- default: return null;
- }
- }
-
- public int getCode() {
- return mCode;
- }
-
- @Nullable
- public String getLabel() {
- return mLabel;
- }
-
- @Nullable
- public String getHintLabel() {
- return mHintLabel;
- }
-
- @Nullable
- public MoreKeySpec[] getMoreKeys() {
- return mMoreKeys;
- }
-
- public void markAsLeftEdge(final KeyboardParams params) {
- mHitBox.left = params.mLeftPadding;
- }
-
- public void markAsRightEdge(final KeyboardParams params) {
- mHitBox.right = params.mOccupiedWidth - params.mRightPadding;
- }
-
- public void markAsTopEdge(final KeyboardParams params) {
- mHitBox.top = params.mTopPadding;
- }
-
- public void markAsBottomEdge(final KeyboardParams params) {
- mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
- }
-
- public final boolean isSpacer() {
- return this instanceof Spacer;
- }
-
- public final boolean isActionKey() {
- return mBackgroundType == BACKGROUND_TYPE_ACTION;
- }
-
- public final boolean isShift() {
- return mCode == CODE_SHIFT;
- }
-
- public final boolean isModifier() {
- return mCode == CODE_SHIFT || mCode == CODE_SWITCH_ALPHA_SYMBOL;
- }
-
- public final boolean isRepeatable() {
- return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0;
- }
-
- public final boolean noKeyPreview() {
- return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0;
- }
-
- public final boolean altCodeWhileTyping() {
- return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0;
- }
-
- public final boolean isLongPressEnabled() {
- // We need not start long press timer on the key which has activated shifted letter.
- return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0
- && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
- }
-
- public KeyVisualAttributes getVisualAttributes() {
- return mKeyVisualAttributes;
- }
-
- @Nonnull
- public final Typeface selectTypeface(final KeyDrawParams params) {
- switch (mLabelFlags & LABEL_FLAGS_FONT_MASK) {
- case LABEL_FLAGS_FONT_NORMAL:
- return Typeface.DEFAULT;
- case LABEL_FLAGS_FONT_MONO_SPACE:
- return Typeface.MONOSPACE;
- case LABEL_FLAGS_FONT_DEFAULT:
- default:
- // The type-face is specified by keyTypeface attribute.
- return params.mTypeface;
- }
- }
-
- public final int selectTextSize(final KeyDrawParams params) {
- switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) {
- case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO:
- return params.mLetterSize;
- case LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO:
- return params.mLargeLetterSize;
- case LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO:
- return params.mLabelSize;
- case LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO:
- return params.mHintLabelSize;
- default: // No follow key ratio flag specified.
- return StringUtils.codePointCount(mLabel) == 1 ? params.mLetterSize : params.mLabelSize;
- }
- }
-
- public final int selectTextColor(final KeyDrawParams params) {
- if ((mLabelFlags & LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR) != 0) {
- return params.mFunctionalTextColor;
- }
- return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
- }
-
- public final int selectHintTextSize(final KeyDrawParams params) {
- if (hasHintLabel()) {
- return params.mHintLabelSize;
- }
- if (hasShiftedLetterHint()) {
- return params.mShiftedLetterHintSize;
- }
- return params.mHintLetterSize;
- }
-
- public final int selectHintTextColor(final KeyDrawParams params) {
- if (hasHintLabel()) {
- return params.mHintLabelColor;
- }
- if (hasShiftedLetterHint()) {
- return isShiftedLetterActivated() ? params.mShiftedLetterHintActivatedColor
- : params.mShiftedLetterHintInactivatedColor;
- }
- return params.mHintLetterColor;
- }
-
- public final int selectMoreKeyTextSize(final KeyDrawParams params) {
- return hasLabelsInMoreKeys() ? params.mLabelSize : params.mLetterSize;
- }
-
- public final String getPreviewLabel() {
- return isShiftedLetterActivated() ? mHintLabel : mLabel;
- }
-
- private boolean previewHasLetterSize() {
- return (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO) != 0
- || StringUtils.codePointCount(getPreviewLabel()) == 1;
- }
-
- public final int selectPreviewTextSize(final KeyDrawParams params) {
- if (previewHasLetterSize()) {
- return params.mPreviewTextSize;
- }
- return params.mLetterSize;
- }
-
- @Nonnull
- public Typeface selectPreviewTypeface(final KeyDrawParams params) {
- if (previewHasLetterSize()) {
- return selectTypeface(params);
- }
- return Typeface.DEFAULT_BOLD;
- }
-
- public final boolean isAlignHintLabelToBottom(final int defaultFlags) {
- return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_ALIGN_HINT_LABEL_TO_BOTTOM) != 0;
- }
-
- public final boolean isAlignIconToBottom() {
- return (mLabelFlags & LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM) != 0;
- }
-
- public final boolean isAlignLabelOffCenter() {
- return (mLabelFlags & LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER) != 0;
- }
-
- public final boolean hasPopupHint() {
- return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0;
- }
-
- public final boolean hasShiftedLetterHint() {
- return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0
- && !TextUtils.isEmpty(mHintLabel);
- }
-
- public final boolean hasHintLabel() {
- return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0;
- }
-
- public final boolean needsAutoXScale() {
- return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0;
- }
-
- public final boolean needsAutoScale() {
- return (mLabelFlags & LABEL_FLAGS_AUTO_SCALE) == LABEL_FLAGS_AUTO_SCALE;
- }
-
- public final boolean needsToKeepBackgroundAspectRatio(final int defaultFlags) {
- return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO) != 0;
- }
-
- public final boolean hasCustomActionLabel() {
- return (mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0;
- }
-
- private final boolean isShiftedLetterActivated() {
- return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0
- && !TextUtils.isEmpty(mHintLabel);
- }
-
- public final int getMoreKeysColumnNumber() {
- return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_NUMBER_MASK;
- }
-
- public final boolean isMoreKeysFixedColumn() {
- return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN) != 0;
- }
-
- public final boolean isMoreKeysFixedOrder() {
- return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_ORDER) != 0;
- }
-
- public final boolean hasLabelsInMoreKeys() {
- return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_HAS_LABELS) != 0;
- }
-
- public final int getMoreKeyLabelFlags() {
- final int labelSizeFlag = hasLabelsInMoreKeys()
- ? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO
- : LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO;
- return labelSizeFlag | LABEL_FLAGS_AUTO_X_SCALE;
- }
-
- public final boolean needsDividersInMoreKeys() {
- return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NEEDS_DIVIDERS) != 0;
- }
-
- public final boolean hasNoPanelAutoMoreKey() {
- return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY) != 0;
- }
-
- @Nullable
- public final String getOutputText() {
- final OptionalAttributes attrs = mOptionalAttributes;
- return (attrs != null) ? attrs.mOutputText : null;
- }
-
- public final int getAltCode() {
- final OptionalAttributes attrs = mOptionalAttributes;
- return (attrs != null) ? attrs.mAltCode : CODE_UNSPECIFIED;
- }
-
- public int getIconId() {
- return mIconId;
- }
-
- @Nullable
- public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
- final OptionalAttributes attrs = mOptionalAttributes;
- final int disabledIconId = (attrs != null) ? attrs.mDisabledIconId : ICON_UNDEFINED;
- final int iconId = mEnabled ? getIconId() : disabledIconId;
- final Drawable icon = iconSet.getIconDrawable(iconId);
- if (icon != null) {
- icon.setAlpha(alpha);
- }
- return icon;
- }
-
- @Nullable
- public Drawable getPreviewIcon(final KeyboardIconsSet iconSet) {
- return iconSet.getIconDrawable(getIconId());
- }
-
- /**
- * Gets the width of the key in pixels, excluding the gap.
- * @return The width of the key in pixels, excluding the gap.
- */
- public int getWidth() {
- return mWidth;
- }
-
- /**
- * Gets the height of the key in pixels, excluding the gap.
- * @return The height of the key in pixels, excluding the gap.
- */
- public int getHeight() {
- return mHeight;
- }
-
- /**
- * The combined width in pixels of the horizontal gaps belonging to this key, both above and
- * below. I.e., getWidth() + getHorizontalGap() = total width belonging to the key.
- * @return Horizontal gap belonging to this key.
- */
- public int getHorizontalGap() {
- return mHorizontalGap;
- }
-
- /**
- * The combined height in pixels of the vertical gaps belonging to this key, both above and
- * below. I.e., getHeight() + getVerticalGap() = total height belonging to the key.
- * @return Vertical gap belonging to this key.
- */
- public int getVerticalGap() {
- return mVerticalGap;
- }
-
- /**
- * Gets the x-coordinate of the top-left corner of the key in pixels, excluding the gap.
- * @return The x-coordinate of the top-left corner of the key in pixels, excluding the gap.
- */
- public int getX() {
- return mX;
- }
-
- /**
- * Gets the y-coordinate of the top-left corner of the key in pixels, excluding the gap.
- * @return The y-coordinate of the top-left corner of the key in pixels, excluding the gap.
- */
- public int getY() {
- return mY;
- }
-
- public final int getDrawX() {
- final int x = getX();
- final OptionalAttributes attrs = mOptionalAttributes;
- return (attrs == null) ? x : x + attrs.mVisualInsetsLeft;
- }
-
- public final int getDrawWidth() {
- final OptionalAttributes attrs = mOptionalAttributes;
- return (attrs == null) ? mWidth
- : mWidth - attrs.mVisualInsetsLeft - attrs.mVisualInsetsRight;
- }
-
- /**
- * Informs the key that it has been pressed, in case it needs to change its appearance or
- * state.
- * @see #onReleased()
- */
- public void onPressed() {
- mPressed = true;
- }
-
- /**
- * Informs the key that it has been released, in case it needs to change its appearance or
- * state.
- * @see #onPressed()
- */
- public void onReleased() {
- mPressed = false;
- }
-
- public final boolean isEnabled() {
- return mEnabled;
- }
-
- public void setEnabled(final boolean enabled) {
- mEnabled = enabled;
- }
-
- @Nonnull
- public Rect getHitBox() {
- return mHitBox;
- }
-
- /**
- * Detects if a point falls on this key.
- * @param x the x-coordinate of the point
- * @param y the y-coordinate of the point
- * @return whether or not the point falls on the key. If the key is attached to an edge, it
- * will assume that all points between the key and the edge are considered to be on the key.
- * @see #markAsLeftEdge(KeyboardParams) etc.
- */
- public boolean isOnKey(final int x, final int y) {
- return mHitBox.contains(x, y);
- }
-
- /**
- * Returns the square of the distance to the nearest edge of the key and the given point.
- * @param x the x-coordinate of the point
- * @param y the y-coordinate of the point
- * @return the square of the distance of the point from the nearest edge of the key
- */
- public int squaredDistanceToEdge(final int x, final int y) {
- final int left = getX();
- final int right = left + mWidth;
- final int top = getY();
- final int bottom = top + mHeight;
- final int edgeX = x < left ? left : (x > right ? right : x);
- final int edgeY = y < top ? top : (y > bottom ? bottom : y);
- final int dx = x - edgeX;
- final int dy = y - edgeY;
- return dx * dx + dy * dy;
- }
-
- static class KeyBackgroundState {
- private final int[] mReleasedState;
- private final int[] mPressedState;
-
- private KeyBackgroundState(final int ... attrs) {
- mReleasedState = attrs;
- mPressedState = Arrays.copyOf(attrs, attrs.length + 1);
- mPressedState[attrs.length] = android.R.attr.state_pressed;
- }
-
- public int[] getState(final boolean pressed) {
- return pressed ? mPressedState : mReleasedState;
- }
-
- public static final KeyBackgroundState[] STATES = {
- // 0: BACKGROUND_TYPE_EMPTY
- new KeyBackgroundState(android.R.attr.state_empty),
- // 1: BACKGROUND_TYPE_NORMAL
- new KeyBackgroundState(),
- // 2: BACKGROUND_TYPE_FUNCTIONAL
- new KeyBackgroundState(),
- // 3: BACKGROUND_TYPE_STICKY_OFF
- new KeyBackgroundState(android.R.attr.state_checkable),
- // 4: BACKGROUND_TYPE_STICKY_ON
- new KeyBackgroundState(android.R.attr.state_checkable, android.R.attr.state_checked),
- // 5: BACKGROUND_TYPE_ACTION
- new KeyBackgroundState(android.R.attr.state_active),
- // 6: BACKGROUND_TYPE_SPACEBAR
- new KeyBackgroundState(),
- };
- }
-
- /**
- * Returns the background drawable for the key, based on the current state and type of the key.
- * @return the background drawable of the key.
- * @see android.graphics.drawable.StateListDrawable#setState(int[])
- */
- @Nonnull
- public final Drawable selectBackgroundDrawable(@Nonnull final Drawable keyBackground,
- @Nonnull final Drawable functionalKeyBackground,
- @Nonnull final Drawable spacebarBackground) {
- final Drawable background;
- if (mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL) {
- background = functionalKeyBackground;
- } else if (mBackgroundType == BACKGROUND_TYPE_SPACEBAR) {
- background = spacebarBackground;
- } else {
- background = keyBackground;
- }
- final int[] state = KeyBackgroundState.STATES[mBackgroundType].getState(mPressed);
- background.setState(state);
- return background;
- }
-
- public static class Spacer extends Key {
- public Spacer(final TypedArray keyAttr, final KeyStyle keyStyle,
- final KeyboardParams params, final KeyboardRow row) {
- super(null /* keySpec */, keyAttr, keyStyle, params, row);
- }
-
- /**
- * This constructor is being used only for divider in more keys keyboard.
- */
- protected Spacer(final KeyboardParams params, final int x, final int y, final int width,
- final int height) {
- super(null /* label */, ICON_UNDEFINED, CODE_UNSPECIFIED, null /* outputText */,
- null /* hintLabel */, 0 /* labelFlags */, BACKGROUND_TYPE_EMPTY, x, y, width,
- height, params.mHorizontalGap, params.mVerticalGap);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
deleted file mode 100644
index 87368d4ef..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-/**
- * This class handles key detection.
- */
-public class KeyDetector {
- private final int mKeyHysteresisDistanceSquared;
- private final int mKeyHysteresisDistanceForSlidingModifierSquared;
-
- private Keyboard mKeyboard;
- private int mCorrectionX;
- private int mCorrectionY;
-
- public KeyDetector() {
- this(0.0f /* keyHysteresisDistance */, 0.0f /* keyHysteresisDistanceForSlidingModifier */);
- }
-
- /**
- * Key detection object constructor with key hysteresis distances.
- *
- * @param keyHysteresisDistance if the pointer movement distance is smaller than this, the
- * movement will not be handled as meaningful movement. The unit is pixel.
- * @param keyHysteresisDistanceForSlidingModifier the same parameter for sliding input that
- * starts from a modifier key such as shift and symbols key.
- */
- public KeyDetector(final float keyHysteresisDistance,
- final float keyHysteresisDistanceForSlidingModifier) {
- mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
- mKeyHysteresisDistanceForSlidingModifierSquared = (int)(
- keyHysteresisDistanceForSlidingModifier * keyHysteresisDistanceForSlidingModifier);
- }
-
- public void setKeyboard(final Keyboard keyboard, final float correctionX,
- final float correctionY) {
- if (keyboard == null) {
- throw new NullPointerException();
- }
- mCorrectionX = (int)correctionX;
- mCorrectionY = (int)correctionY;
- mKeyboard = keyboard;
- }
-
- public int getKeyHysteresisDistanceSquared(final boolean isSlidingFromModifier) {
- return isSlidingFromModifier
- ? mKeyHysteresisDistanceForSlidingModifierSquared : mKeyHysteresisDistanceSquared;
- }
-
- public int getTouchX(final int x) {
- return x + mCorrectionX;
- }
-
- // TODO: Remove vertical correction.
- public int getTouchY(final int y) {
- return y + mCorrectionY;
- }
-
- public Keyboard getKeyboard() {
- return mKeyboard;
- }
-
- public boolean alwaysAllowsKeySelectionByDraggingFinger() {
- return false;
- }
-
- /**
- * Detect the key whose hitbox the touch point is in.
- *
- * @param x The x-coordinate of a touch point
- * @param y The y-coordinate of a touch point
- * @return the key that the touch point hits.
- */
- public Key detectHitKey(final int x, final int y) {
- if (mKeyboard == null) {
- return null;
- }
- final int touchX = getTouchX(x);
- final int touchY = getTouchY(y);
-
- int minDistance = Integer.MAX_VALUE;
- Key primaryKey = null;
- for (final Key key: mKeyboard.getNearestKeys(touchX, touchY)) {
- // An edge key always has its enlarged hitbox to respond to an event that occurred in
- // the empty area around the key. (@see Key#markAsLeftEdge(KeyboardParams)} etc.)
- if (!key.isOnKey(touchX, touchY)) {
- continue;
- }
- final int distance = key.squaredDistanceToEdge(touchX, touchY);
- if (distance > minDistance) {
- continue;
- }
- // To take care of hitbox overlaps, we compare key's code here too.
- if (primaryKey == null || distance < minDistance
- || key.getCode() > primaryKey.getCode()) {
- minDistance = distance;
- primaryKey = key;
- }
- }
- return primaryKey;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
deleted file mode 100644
index 7318d4738..000000000
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.util.SparseArray;
-
-import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
-import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
- * consists of rows of keys.
- * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
- * <pre>
- * &lt;Keyboard
- * latin:keyWidth="10%p"
- * latin:rowHeight="50px"
- * latin:horizontalGap="2%p"
- * latin:verticalGap="2%p" &gt;
- * &lt;Row latin:keyWidth="10%p" &gt;
- * &lt;Key latin:keyLabel="A" /&gt;
- * ...
- * &lt;/Row&gt;
- * ...
- * &lt;/Keyboard&gt;
- * </pre>
- */
-public class Keyboard {
- @Nonnull
- public final KeyboardId mId;
- public final int mThemeId;
-
- /** Total height of the keyboard, including the padding and keys */
- public final int mOccupiedHeight;
- /** Total width of the keyboard, including the padding and keys */
- public final int mOccupiedWidth;
-
- /** Base height of the keyboard, used to calculate rows' height */
- public final int mBaseHeight;
- /** Base width of the keyboard, used to calculate keys' width */
- public final int mBaseWidth;
-
- /** The padding above the keyboard */
- public final int mTopPadding;
- /** Default gap between rows */
- public final int mVerticalGap;
-
- /** Per keyboard key visual parameters */
- public final KeyVisualAttributes mKeyVisualAttributes;
-
- public final int mMostCommonKeyHeight;
- public final int mMostCommonKeyWidth;
-
- /** More keys keyboard template */
- public final int mMoreKeysTemplate;
-
- /** Maximum column for more keys keyboard */
- public final int mMaxMoreKeysKeyboardColumn;
-
- /** List of keys in this keyboard */
- @Nonnull
- private final List<Key> mSortedKeys;
- @Nonnull
- public final List<Key> mShiftKeys;
- @Nonnull
- public final List<Key> mAltCodeKeysWhileTyping;
- @Nonnull
- public final KeyboardIconsSet mIconsSet;
-
- private final SparseArray<Key> mKeyCache = new SparseArray<>();
-
- @Nonnull
- private final ProximityInfo mProximityInfo;
- @Nonnull
- private final KeyboardLayout mKeyboardLayout;
-
- private final boolean mProximityCharsCorrectionEnabled;
-
- public Keyboard(@Nonnull final KeyboardParams params) {
- mId = params.mId;
- mThemeId = params.mThemeId;
- mOccupiedHeight = params.mOccupiedHeight;
- mOccupiedWidth = params.mOccupiedWidth;
- mBaseHeight = params.mBaseHeight;
- mBaseWidth = params.mBaseWidth;
- mMostCommonKeyHeight = params.mMostCommonKeyHeight;
- mMostCommonKeyWidth = params.mMostCommonKeyWidth;
- mMoreKeysTemplate = params.mMoreKeysTemplate;
- mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
- mKeyVisualAttributes = params.mKeyVisualAttributes;
- mTopPadding = params.mTopPadding;
- mVerticalGap = params.mVerticalGap;
-
- mSortedKeys = Collections.unmodifiableList(new ArrayList<>(params.mSortedKeys));
- mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
- mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping);
- mIconsSet = params.mIconsSet;
-
- mProximityInfo = new ProximityInfo(params.GRID_WIDTH, params.GRID_HEIGHT,
- mOccupiedWidth, mOccupiedHeight, mMostCommonKeyWidth, mMostCommonKeyHeight,
- mSortedKeys, params.mTouchPositionCorrection);
- mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
- mKeyboardLayout = KeyboardLayout.newKeyboardLayout(mSortedKeys, mMostCommonKeyWidth,
- mMostCommonKeyHeight, mOccupiedWidth, mOccupiedHeight);
- }
-
- protected Keyboard(@Nonnull final Keyboard keyboard) {
- mId = keyboard.mId;
- mThemeId = keyboard.mThemeId;
- mOccupiedHeight = keyboard.mOccupiedHeight;
- mOccupiedWidth = keyboard.mOccupiedWidth;
- mBaseHeight = keyboard.mBaseHeight;
- mBaseWidth = keyboard.mBaseWidth;
- mMostCommonKeyHeight = keyboard.mMostCommonKeyHeight;
- mMostCommonKeyWidth = keyboard.mMostCommonKeyWidth;
- mMoreKeysTemplate = keyboard.mMoreKeysTemplate;
- mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn;
- mKeyVisualAttributes = keyboard.mKeyVisualAttributes;
- mTopPadding = keyboard.mTopPadding;
- mVerticalGap = keyboard.mVerticalGap;
-
- mSortedKeys = keyboard.mSortedKeys;
- mShiftKeys = keyboard.mShiftKeys;
- mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping;
- mIconsSet = keyboard.mIconsSet;
-
- mProximityInfo = keyboard.mProximityInfo;
- mProximityCharsCorrectionEnabled = keyboard.mProximityCharsCorrectionEnabled;
- mKeyboardLayout = keyboard.mKeyboardLayout;
- }
-
- public boolean hasProximityCharsCorrection(final int code) {
- if (!mProximityCharsCorrectionEnabled) {
- return false;
- }
- // Note: The native code has the main keyboard layout only at this moment.
- // TODO: Figure out how to handle proximity characters information of all layouts.
- final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = (
- mId.mElementId == KeyboardId.ELEMENT_ALPHABET
- || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
- return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code);
- }
-
- @Nonnull
- public ProximityInfo getProximityInfo() {
- return mProximityInfo;
- }
-
- @Nonnull
- public KeyboardLayout getKeyboardLayout() {
- return mKeyboardLayout;
- }
-
- /**
- * Return the sorted list of keys of this keyboard.
- * The keys are sorted from top-left to bottom-right order.
- * The list may contain {@link Key.Spacer} object as well.
- * @return the sorted unmodifiable list of {@link Key}s of this keyboard.
- */
- @Nonnull
- public List<Key> getSortedKeys() {
- return mSortedKeys;
- }
-
- @Nullable
- public Key getKey(final int code) {
- if (code == Constants.CODE_UNSPECIFIED) {
- return null;
- }
- synchronized (mKeyCache) {
- final int index = mKeyCache.indexOfKey(code);
- if (index >= 0) {
- return mKeyCache.valueAt(index);
- }
-
- for (final Key key : getSortedKeys()) {
- if (key.getCode() == code) {
- mKeyCache.put(code, key);
- return key;
- }
- }
- mKeyCache.put(code, null);
- return null;
- }
- }
-
- public boolean hasKey(@Nonnull final Key aKey) {
- if (mKeyCache.indexOfValue(aKey) >= 0) {
- return true;
- }
-
- for (final Key key : getSortedKeys()) {
- if (key == aKey) {
- mKeyCache.put(key.getCode(), key);
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- return mId.toString();
- }
-
- /**
- * Returns the array of the keys that are closest to the given point.
- * @param x the x-coordinate of the point
- * @param y the y-coordinate of the point
- * @return the list of the nearest keys to the given point. If the given
- * point is out of range, then an array of size zero is returned.
- */
- @Nonnull
- public List<Key> getNearestKeys(final int x, final int y) {
- // Avoid dead pixels at edges of the keyboard
- final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
- final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
- return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
- }
-
- @Nonnull
- public int[] getCoordinates(@Nonnull final int[] codePoints) {
- final int length = codePoints.length;
- final int[] coordinates = CoordinateUtils.newCoordinateArray(length);
- for (int i = 0; i < length; ++i) {
- final Key key = getKey(codePoints[i]);
- if (null != key) {
- CoordinateUtils.setXYInArray(coordinates, i,
- key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2);
- } else {
- CoordinateUtils.setXYInArray(coordinates, i,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
- }
- }
- return coordinates;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
deleted file mode 100644
index cdd632bc8..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.InputPointers;
-
-public interface KeyboardActionListener {
- /**
- * Called when the user presses a key. This is sent before the {@link #onCodeInput} is called.
- * For keys that repeat, this is only called once.
- *
- * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid key,
- * the value will be zero.
- * @param repeatCount how many times the key was repeated. Zero if it is the first press.
- * @param isSinglePointer true if pressing has occurred while no other key is being pressed.
- */
- public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer);
-
- /**
- * Called when the user releases a key. This is sent after the {@link #onCodeInput} is called.
- * For keys that repeat, this is only called once.
- *
- * @param primaryCode the code of the key that was released
- * @param withSliding true if releasing has occurred because the user slid finger from the key
- * to other key without releasing the finger.
- */
- public void onReleaseKey(int primaryCode, boolean withSliding);
-
- /**
- * Send a key code to the listener.
- *
- * @param primaryCode this is the code of the key that was pressed
- * @param x x-coordinate pixel of touched event. If {@link #onCodeInput} is not called by
- * {@link PointerTracker} or so, the value should be
- * {@link Constants#NOT_A_COORDINATE}. If it's called on insertion from the
- * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}.
- * @param y y-coordinate pixel of touched event. If {@link #onCodeInput} is not called by
- * {@link PointerTracker} or so, the value should be
- * {@link Constants#NOT_A_COORDINATE}.If it's called on insertion from the
- * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}.
- * @param isKeyRepeat true if this is a key repeat, false otherwise
- */
- // TODO: change this to send an Event object instead
- public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat);
-
- /**
- * Sends a string of characters to the listener.
- *
- * @param text the string of characters to be registered.
- */
- public void onTextInput(String text);
-
- /**
- * Called when user started batch input.
- */
- public void onStartBatchInput();
-
- /**
- * Sends the ongoing batch input points data.
- * @param batchPointers the batch input points representing the user input
- */
- public void onUpdateBatchInput(InputPointers batchPointers);
-
- /**
- * Sends the final batch input points data.
- *
- * @param batchPointers the batch input points representing the user input
- */
- public void onEndBatchInput(InputPointers batchPointers);
-
- public void onCancelBatchInput();
-
- /**
- * Called when user released a finger outside any key.
- */
- public void onCancelInput();
-
- /**
- * Called when user finished sliding key input.
- */
- public void onFinishSlidingInput();
-
- /**
- * Send a non-"code input" custom request to the listener.
- * @return true if the request has been consumed, false otherwise.
- */
- public boolean onCustomRequest(int requestCode);
-
- public static final KeyboardActionListener EMPTY_LISTENER = new Adapter();
-
- public static class Adapter implements KeyboardActionListener {
- @Override
- public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer) {}
- @Override
- public void onReleaseKey(int primaryCode, boolean withSliding) {}
- @Override
- public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat) {}
- @Override
- public void onTextInput(String text) {}
- @Override
- public void onStartBatchInput() {}
- @Override
- public void onUpdateBatchInput(InputPointers batchPointers) {}
- @Override
- public void onEndBatchInput(InputPointers batchPointers) {}
- @Override
- public void onCancelBatchInput() {}
- @Override
- public void onCancelInput() {}
- @Override
- public void onFinishSlidingInput() {}
- @Override
- public boolean onCustomRequest(int requestCode) {
- return false;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
deleted file mode 100644
index 7352f911b..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
-
-import android.text.InputType;
-import android.text.TextUtils;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.compat.EditorInfoCompatUtils;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.utils.InputTypeUtils;
-
-import java.util.Arrays;
-import java.util.Locale;
-
-/**
- * Unique identifier for each keyboard type.
- */
-public final class KeyboardId {
- public static final int MODE_TEXT = 0;
- public static final int MODE_URL = 1;
- public static final int MODE_EMAIL = 2;
- public static final int MODE_IM = 3;
- public static final int MODE_PHONE = 4;
- public static final int MODE_NUMBER = 5;
- public static final int MODE_DATE = 6;
- public static final int MODE_TIME = 7;
- public static final int MODE_DATETIME = 8;
-
- public static final int ELEMENT_ALPHABET = 0;
- public static final int ELEMENT_ALPHABET_MANUAL_SHIFTED = 1;
- public static final int ELEMENT_ALPHABET_AUTOMATIC_SHIFTED = 2;
- public static final int ELEMENT_ALPHABET_SHIFT_LOCKED = 3;
- public static final int ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED = 4;
- public static final int ELEMENT_SYMBOLS = 5;
- public static final int ELEMENT_SYMBOLS_SHIFTED = 6;
- public static final int ELEMENT_PHONE = 7;
- public static final int ELEMENT_PHONE_SYMBOLS = 8;
- public static final int ELEMENT_NUMBER = 9;
- public static final int ELEMENT_EMOJI_RECENTS = 10;
- public static final int ELEMENT_EMOJI_CATEGORY1 = 11;
- public static final int ELEMENT_EMOJI_CATEGORY2 = 12;
- public static final int ELEMENT_EMOJI_CATEGORY3 = 13;
- public static final int ELEMENT_EMOJI_CATEGORY4 = 14;
- public static final int ELEMENT_EMOJI_CATEGORY5 = 15;
- public static final int ELEMENT_EMOJI_CATEGORY6 = 16;
- public static final int ELEMENT_EMOJI_CATEGORY7 = 17;
- public static final int ELEMENT_EMOJI_CATEGORY8 = 18;
- public static final int ELEMENT_EMOJI_CATEGORY9 = 19;
- public static final int ELEMENT_EMOJI_CATEGORY10 = 20;
- public static final int ELEMENT_EMOJI_CATEGORY11 = 21;
- public static final int ELEMENT_EMOJI_CATEGORY12 = 22;
- public static final int ELEMENT_EMOJI_CATEGORY13 = 23;
- public static final int ELEMENT_EMOJI_CATEGORY14 = 24;
- public static final int ELEMENT_EMOJI_CATEGORY15 = 25;
- public static final int ELEMENT_EMOJI_CATEGORY16 = 26;
-
- public final RichInputMethodSubtype mSubtype;
- public final int mWidth;
- public final int mHeight;
- public final int mMode;
- public final int mElementId;
- public final EditorInfo mEditorInfo;
- public final boolean mClobberSettingsKey;
- public final boolean mLanguageSwitchKeyEnabled;
- public final String mCustomActionLabel;
- public final boolean mHasShortcutKey;
- public final boolean mIsSplitLayout;
-
- private final int mHashCode;
-
- public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
- mSubtype = params.mSubtype;
- mWidth = params.mKeyboardWidth;
- mHeight = params.mKeyboardHeight;
- mMode = params.mMode;
- mElementId = elementId;
- mEditorInfo = params.mEditorInfo;
- mClobberSettingsKey = params.mNoSettingsKey;
- mLanguageSwitchKeyEnabled = params.mLanguageSwitchKeyEnabled;
- mCustomActionLabel = (mEditorInfo.actionLabel != null)
- ? mEditorInfo.actionLabel.toString() : null;
- mHasShortcutKey = params.mVoiceInputKeyEnabled;
- mIsSplitLayout = params.mIsSplitLayoutEnabled;
-
- mHashCode = computeHashCode(this);
- }
-
- private static int computeHashCode(final KeyboardId id) {
- return Arrays.hashCode(new Object[] {
- id.mElementId,
- id.mMode,
- id.mWidth,
- id.mHeight,
- id.passwordInput(),
- id.mClobberSettingsKey,
- id.mHasShortcutKey,
- id.mLanguageSwitchKeyEnabled,
- id.isMultiLine(),
- id.imeAction(),
- id.mCustomActionLabel,
- id.navigateNext(),
- id.navigatePrevious(),
- id.mSubtype,
- id.mIsSplitLayout
- });
- }
-
- private boolean equals(final KeyboardId other) {
- if (other == this)
- return true;
- return other.mElementId == mElementId
- && other.mMode == mMode
- && other.mWidth == mWidth
- && other.mHeight == mHeight
- && other.passwordInput() == passwordInput()
- && other.mClobberSettingsKey == mClobberSettingsKey
- && other.mHasShortcutKey == mHasShortcutKey
- && other.mLanguageSwitchKeyEnabled == mLanguageSwitchKeyEnabled
- && other.isMultiLine() == isMultiLine()
- && other.imeAction() == imeAction()
- && TextUtils.equals(other.mCustomActionLabel, mCustomActionLabel)
- && other.navigateNext() == navigateNext()
- && other.navigatePrevious() == navigatePrevious()
- && other.mSubtype.equals(mSubtype)
- && other.mIsSplitLayout == mIsSplitLayout;
- }
-
- private static boolean isAlphabetKeyboard(final int elementId) {
- return elementId < ELEMENT_SYMBOLS;
- }
-
- public boolean isAlphabetKeyboard() {
- return isAlphabetKeyboard(mElementId);
- }
-
- public boolean navigateNext() {
- return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0
- || imeAction() == EditorInfo.IME_ACTION_NEXT;
- }
-
- public boolean navigatePrevious() {
- return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0
- || imeAction() == EditorInfo.IME_ACTION_PREVIOUS;
- }
-
- public boolean passwordInput() {
- final int inputType = mEditorInfo.inputType;
- return InputTypeUtils.isPasswordInputType(inputType)
- || InputTypeUtils.isVisiblePasswordInputType(inputType);
- }
-
- public boolean isMultiLine() {
- return (mEditorInfo.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
- }
-
- public int imeAction() {
- return InputTypeUtils.getImeOptionsActionIdFromEditorInfo(mEditorInfo);
- }
-
- public Locale getLocale() {
- return mSubtype.getLocale();
- }
-
- @Override
- public boolean equals(final Object other) {
- return other instanceof KeyboardId && equals((KeyboardId) other);
- }
-
- @Override
- public int hashCode() {
- return mHashCode;
- }
-
- @Override
- public String toString() {
- return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]",
- elementIdToName(mElementId),
- mSubtype.getLocale(),
- mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
- mWidth, mHeight,
- modeName(mMode),
- actionName(imeAction()),
- (navigateNext() ? " navigateNext" : ""),
- (navigatePrevious() ? " navigatePrevious" : ""),
- (mClobberSettingsKey ? " clobberSettingsKey" : ""),
- (passwordInput() ? " passwordInput" : ""),
- (mHasShortcutKey ? " hasShortcutKey" : ""),
- (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""),
- (isMultiLine() ? " isMultiLine" : ""),
- (mIsSplitLayout ? " isSplitLayout" : "")
- );
- }
-
- public static boolean equivalentEditorInfoForKeyboard(final EditorInfo a, final EditorInfo b) {
- if (a == null && b == null) return true;
- if (a == null || b == null) return false;
- return a.inputType == b.inputType
- && a.imeOptions == b.imeOptions
- && TextUtils.equals(a.privateImeOptions, b.privateImeOptions);
- }
-
- public static String elementIdToName(final int elementId) {
- switch (elementId) {
- case ELEMENT_ALPHABET: return "alphabet";
- case ELEMENT_ALPHABET_MANUAL_SHIFTED: return "alphabetManualShifted";
- case ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: return "alphabetAutomaticShifted";
- case ELEMENT_ALPHABET_SHIFT_LOCKED: return "alphabetShiftLocked";
- case ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: return "alphabetShiftLockShifted";
- case ELEMENT_SYMBOLS: return "symbols";
- case ELEMENT_SYMBOLS_SHIFTED: return "symbolsShifted";
- case ELEMENT_PHONE: return "phone";
- case ELEMENT_PHONE_SYMBOLS: return "phoneSymbols";
- case ELEMENT_NUMBER: return "number";
- case ELEMENT_EMOJI_RECENTS: return "emojiRecents";
- case ELEMENT_EMOJI_CATEGORY1: return "emojiCategory1";
- case ELEMENT_EMOJI_CATEGORY2: return "emojiCategory2";
- case ELEMENT_EMOJI_CATEGORY3: return "emojiCategory3";
- case ELEMENT_EMOJI_CATEGORY4: return "emojiCategory4";
- case ELEMENT_EMOJI_CATEGORY5: return "emojiCategory5";
- case ELEMENT_EMOJI_CATEGORY6: return "emojiCategory6";
- case ELEMENT_EMOJI_CATEGORY7: return "emojiCategory7";
- case ELEMENT_EMOJI_CATEGORY8: return "emojiCategory8";
- case ELEMENT_EMOJI_CATEGORY9: return "emojiCategory9";
- case ELEMENT_EMOJI_CATEGORY10: return "emojiCategory10";
- case ELEMENT_EMOJI_CATEGORY11: return "emojiCategory11";
- case ELEMENT_EMOJI_CATEGORY12: return "emojiCategory12";
- case ELEMENT_EMOJI_CATEGORY13: return "emojiCategory13";
- case ELEMENT_EMOJI_CATEGORY14: return "emojiCategory14";
- case ELEMENT_EMOJI_CATEGORY15: return "emojiCategory15";
- case ELEMENT_EMOJI_CATEGORY16: return "emojiCategory16";
- default: return null;
- }
- }
-
- public static String modeName(final int mode) {
- switch (mode) {
- case MODE_TEXT: return "text";
- case MODE_URL: return "url";
- case MODE_EMAIL: return "email";
- case MODE_IM: return "im";
- case MODE_PHONE: return "phone";
- case MODE_NUMBER: return "number";
- case MODE_DATE: return "date";
- case MODE_TIME: return "time";
- case MODE_DATETIME: return "datetime";
- default: return null;
- }
- }
-
- public static String actionName(final int actionId) {
- return (actionId == InputTypeUtils.IME_ACTION_CUSTOM_LABEL) ? "actionCustomLabel"
- : EditorInfoCompatUtils.imeActionName(actionId);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayout.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayout.java
deleted file mode 100644
index d0f32078e..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayout.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-/**
- * KeyboardLayout maintains the keyboard layout information.
- */
-public class KeyboardLayout {
-
- private final int[] mKeyCodes;
-
- private final int[] mKeyXCoordinates;
- private final int[] mKeyYCoordinates;
-
- private final int[] mKeyWidths;
- private final int[] mKeyHeights;
-
- public final int mMostCommonKeyWidth;
- public final int mMostCommonKeyHeight;
-
- public final int mKeyboardWidth;
- public final int mKeyboardHeight;
-
- public KeyboardLayout(ArrayList<Key> layoutKeys, int mostCommonKeyWidth,
- int mostCommonKeyHeight, int keyboardWidth, int keyboardHeight) {
- mMostCommonKeyWidth = mostCommonKeyWidth;
- mMostCommonKeyHeight = mostCommonKeyHeight;
- mKeyboardWidth = keyboardWidth;
- mKeyboardHeight = keyboardHeight;
-
- mKeyCodes = new int[layoutKeys.size()];
- mKeyXCoordinates = new int[layoutKeys.size()];
- mKeyYCoordinates = new int[layoutKeys.size()];
- mKeyWidths = new int[layoutKeys.size()];
- mKeyHeights = new int[layoutKeys.size()];
-
- for (int i = 0; i < layoutKeys.size(); i++) {
- Key key = layoutKeys.get(i);
- mKeyCodes[i] = Character.toLowerCase(key.getCode());
- mKeyXCoordinates[i] = key.getX();
- mKeyYCoordinates[i] = key.getY();
- mKeyWidths[i] = key.getWidth();
- mKeyHeights[i] = key.getHeight();
- }
- }
-
- @UsedForTesting
- public int[] getKeyCodes() {
- return mKeyCodes;
- }
-
- /**
- * The x-coordinate for the top-left corner of the keys.
- *
- */
- public int[] getKeyXCoordinates() {
- return mKeyXCoordinates;
- }
-
- /**
- * The y-coordinate for the top-left corner of the keys.
- */
- public int[] getKeyYCoordinates() {
- return mKeyYCoordinates;
- }
-
- /**
- * The widths of the keys which are smaller than the true hit-area due to the gaps
- * between keys. The mostCommonKey(Width/Height) represents the true key width/height
- * including the gaps.
- */
- public int[] getKeyWidths() {
- return mKeyWidths;
- }
-
- /**
- * The heights of the keys which are smaller than the true hit-area due to the gaps
- * between keys. The mostCommonKey(Width/Height) represents the true key width/height
- * including the gaps.
- */
- public int[] getKeyHeights() {
- return mKeyHeights;
- }
-
- /**
- * Factory method to create {@link KeyboardLayout} objects.
- */
- public static KeyboardLayout newKeyboardLayout(@Nonnull final List<Key> sortedKeys,
- int mostCommonKeyWidth, int mostCommonKeyHeight,
- int occupiedWidth, int occupiedHeight) {
- final ArrayList<Key> layoutKeys = new ArrayList<Key>();
- for (final Key key : sortedKeys) {
- if (!ProximityInfo.needsProximityInfo(key)) {
- continue;
- }
- if (key.getCode() != ',') {
- layoutKeys.add(key);
- }
- }
- return new KeyboardLayout(layoutKeys, mostCommonKeyWidth,
- mostCommonKeyHeight, occupiedWidth, occupiedHeight);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
deleted file mode 100644
index 26ff051bb..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import static com.android.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII;
-import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_SETTINGS_KEY;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.text.InputType;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.compat.EditorInfoCompatUtils;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.compat.UserManagerCompatUtils;
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
-import com.android.inputmethod.keyboard.internal.UniqueKeysCache;
-import com.android.inputmethod.latin.InputAttributes;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.InputTypeUtils;
-import com.android.inputmethod.latin.utils.ScriptUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-import com.android.inputmethod.latin.utils.XmlParseUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * This class represents a set of keyboard layouts. Each of them represents a different keyboard
- * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
- * {@link KeyboardLayoutSet} are related to each other.
- * A {@link KeyboardLayoutSet} needs to be created for each
- * {@link android.view.inputmethod.EditorInfo}.
- */
-public final class KeyboardLayoutSet {
- private static final String TAG = KeyboardLayoutSet.class.getSimpleName();
- private static final boolean DEBUG_CACHE = false;
-
- private static final String TAG_KEYBOARD_SET = "KeyboardLayoutSet";
- private static final String TAG_ELEMENT = "Element";
- private static final String TAG_FEATURE = "Feature";
-
- private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_";
-
- private final Context mContext;
- @Nonnull
- private final Params mParams;
-
- // How many layouts we forcibly keep in cache. This only includes ALPHABET (default) and
- // ALPHABET_AUTOMATIC_SHIFTED layouts - other layouts may stay in memory in the map of
- // soft-references, but we forcibly cache this many alphabetic/auto-shifted layouts.
- private static final int FORCIBLE_CACHE_SIZE = 4;
- // By construction of soft references, anything that is also referenced somewhere else
- // will stay in the cache. So we forcibly keep some references in an array to prevent
- // them from disappearing from sKeyboardCache.
- private static final Keyboard[] sForcibleKeyboardCache = new Keyboard[FORCIBLE_CACHE_SIZE];
- private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
- new HashMap<>();
- @Nonnull
- private static final UniqueKeysCache sUniqueKeysCache = UniqueKeysCache.newInstance();
- private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
- new HashMap<>();
-
- @SuppressWarnings("serial")
- public static final class KeyboardLayoutSetException extends RuntimeException {
- public final KeyboardId mKeyboardId;
-
- public KeyboardLayoutSetException(final Throwable cause, final KeyboardId keyboardId) {
- super(cause);
- mKeyboardId = keyboardId;
- }
- }
-
- private static final class ElementParams {
- int mKeyboardXmlId;
- boolean mProximityCharsCorrectionEnabled;
- boolean mSupportsSplitLayout;
- boolean mAllowRedundantMoreKeys;
- public ElementParams() {}
- }
-
- public static final class Params {
- String mKeyboardLayoutSetName;
- int mMode;
- boolean mDisableTouchPositionCorrectionDataForTest;
- // TODO: Use {@link InputAttributes} instead of these variables.
- EditorInfo mEditorInfo;
- boolean mIsPasswordField;
- boolean mVoiceInputKeyEnabled;
- boolean mNoSettingsKey;
- boolean mLanguageSwitchKeyEnabled;
- RichInputMethodSubtype mSubtype;
- boolean mIsSpellChecker;
- int mKeyboardWidth;
- int mKeyboardHeight;
- int mScriptId = ScriptUtils.SCRIPT_LATIN;
- // Indicates if the user has enabled the split-layout preference
- // and the required ProductionFlags are enabled.
- boolean mIsSplitLayoutEnabledByUser;
- // Indicates if split layout is actually enabled, taking into account
- // whether the user has enabled it, and the keyboard layout supports it.
- boolean mIsSplitLayoutEnabled;
- // Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
- final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
- new SparseArray<>();
- }
-
- public static void onSystemLocaleChanged() {
- clearKeyboardCache();
- }
-
- public static void onKeyboardThemeChanged() {
- clearKeyboardCache();
- }
-
- private static void clearKeyboardCache() {
- sKeyboardCache.clear();
- sUniqueKeysCache.clear();
- }
-
- public static int getScriptId(final Resources resources,
- @Nonnull final InputMethodSubtype subtype) {
- final Integer value = sScriptIdsForSubtypes.get(subtype);
- if (null == value) {
- final int scriptId = Builder.readScriptId(resources, subtype);
- sScriptIdsForSubtypes.put(subtype, scriptId);
- return scriptId;
- }
- return value;
- }
-
- KeyboardLayoutSet(final Context context, @Nonnull final Params params) {
- mContext = context;
- mParams = params;
- }
-
- @Nonnull
- public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) {
- final int keyboardLayoutSetElementId;
- switch (mParams.mMode) {
- case KeyboardId.MODE_PHONE:
- if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) {
- keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS;
- } else {
- keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE;
- }
- break;
- case KeyboardId.MODE_NUMBER:
- case KeyboardId.MODE_DATE:
- case KeyboardId.MODE_TIME:
- case KeyboardId.MODE_DATETIME:
- keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER;
- break;
- default:
- keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId;
- break;
- }
-
- ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
- keyboardLayoutSetElementId);
- if (elementParams == null) {
- elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
- KeyboardId.ELEMENT_ALPHABET);
- }
- // Note: The keyboard for each shift state, and mode are represented as an elementName
- // attribute in a keyboard_layout_set XML file. Also each keyboard layout XML resource is
- // specified as an elementKeyboard attribute in the file.
- // The KeyboardId is an internal key for a Keyboard object.
-
- mParams.mIsSplitLayoutEnabled = mParams.mIsSplitLayoutEnabledByUser
- && elementParams.mSupportsSplitLayout;
- final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams);
- try {
- return getKeyboard(elementParams, id);
- } catch (final RuntimeException e) {
- Log.e(TAG, "Can't create keyboard: " + id, e);
- throw new KeyboardLayoutSetException(e, id);
- }
- }
-
- @Nonnull
- private Keyboard getKeyboard(final ElementParams elementParams, final KeyboardId id) {
- final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
- final Keyboard cachedKeyboard = (ref == null) ? null : ref.get();
- if (cachedKeyboard != null) {
- if (DEBUG_CACHE) {
- Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": HIT id=" + id);
- }
- return cachedKeyboard;
- }
-
- final KeyboardBuilder<KeyboardParams> builder =
- new KeyboardBuilder<>(mContext, new KeyboardParams(sUniqueKeysCache));
- sUniqueKeysCache.setEnabled(id.isAlphabetKeyboard());
- builder.setAllowRedundantMoreKes(elementParams.mAllowRedundantMoreKeys);
- final int keyboardXmlId = elementParams.mKeyboardXmlId;
- builder.load(keyboardXmlId, id);
- if (mParams.mDisableTouchPositionCorrectionDataForTest) {
- builder.disableTouchPositionCorrectionDataForTest();
- }
- builder.setProximityCharsCorrectionEnabled(elementParams.mProximityCharsCorrectionEnabled);
- final Keyboard keyboard = builder.build();
- sKeyboardCache.put(id, new SoftReference<>(keyboard));
- if ((id.mElementId == KeyboardId.ELEMENT_ALPHABET
- || id.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)
- && !mParams.mIsSpellChecker) {
- // We only forcibly cache the primary, "ALPHABET", layouts.
- for (int i = sForcibleKeyboardCache.length - 1; i >= 1; --i) {
- sForcibleKeyboardCache[i] = sForcibleKeyboardCache[i - 1];
- }
- sForcibleKeyboardCache[0] = keyboard;
- if (DEBUG_CACHE) {
- Log.d(TAG, "forcing caching of keyboard with id=" + id);
- }
- }
- if (DEBUG_CACHE) {
- Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": "
- + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
- }
- return keyboard;
- }
-
- public int getScriptId() {
- return mParams.mScriptId;
- }
-
- public static final class Builder {
- private final Context mContext;
- private final String mPackageName;
- private final Resources mResources;
-
- private final Params mParams = new Params();
-
- private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
-
- public Builder(final Context context, @Nullable final EditorInfo ei) {
- mContext = context;
- mPackageName = context.getPackageName();
- mResources = context.getResources();
- final Params params = mParams;
-
- final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO;
- params.mMode = getKeyboardMode(editorInfo);
- // TODO: Consolidate those with {@link InputAttributes}.
- params.mEditorInfo = editorInfo;
- params.mIsPasswordField = InputTypeUtils.isPasswordInputType(editorInfo.inputType);
- params.mNoSettingsKey = InputAttributes.inPrivateImeOptions(
- mPackageName, NO_SETTINGS_KEY, editorInfo);
-
- // When the device is still unlocked, features like showing the IME setting app need to
- // be locked down.
- // TODO: Switch to {@code UserManagerCompat.isUserUnlocked()} in the support-v4 library
- // when it becomes publicly available.
- @UserManagerCompatUtils.LockState
- final int lockState = UserManagerCompatUtils.getUserLockState(context);
- if (lockState == UserManagerCompatUtils.LOCK_STATE_LOCKED) {
- params.mNoSettingsKey = true;
- }
- }
-
- public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHeight) {
- mParams.mKeyboardWidth = keyboardWidth;
- mParams.mKeyboardHeight = keyboardHeight;
- return this;
- }
-
- public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) {
- final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype);
- // TODO: Consolidate with {@link InputAttributes}.
- @SuppressWarnings("deprecation")
- final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
- mPackageName, FORCE_ASCII, mParams.mEditorInfo);
- final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(
- mParams.mEditorInfo.imeOptions)
- || deprecatedForceAscii;
- final RichInputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable)
- ? RichInputMethodSubtype.getNoLanguageSubtype()
- : subtype;
- mParams.mSubtype = keyboardSubtype;
- mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
- + keyboardSubtype.getKeyboardLayoutSetName();
- return this;
- }
-
- public Builder setIsSpellChecker(final boolean isSpellChecker) {
- mParams.mIsSpellChecker = isSpellChecker;
- return this;
- }
-
- public Builder setVoiceInputKeyEnabled(final boolean enabled) {
- mParams.mVoiceInputKeyEnabled = enabled;
- return this;
- }
-
- public Builder setLanguageSwitchKeyEnabled(final boolean enabled) {
- mParams.mLanguageSwitchKeyEnabled = enabled;
- return this;
- }
-
- public Builder disableTouchPositionCorrectionData() {
- mParams.mDisableTouchPositionCorrectionDataForTest = true;
- return this;
- }
-
- public Builder setSplitLayoutEnabledByUser(final boolean enabled) {
- mParams.mIsSplitLayoutEnabledByUser = enabled;
- return this;
- }
-
- // Super redux version of reading the script ID for some subtype from Xml.
- static int readScriptId(final Resources resources, final InputMethodSubtype subtype) {
- final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
- + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
- final int xmlId = getXmlId(resources, layoutSetName);
- final XmlResourceParser parser = resources.getXml(xmlId);
- try {
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- // Bovinate through the XML stupidly searching for TAG_FEATURE, and read
- // the script Id from it.
- parser.next();
- final String tag = parser.getName();
- if (TAG_FEATURE.equals(tag)) {
- return readScriptIdFromTagFeature(resources, parser);
- }
- }
- } catch (final IOException | XmlPullParserException e) {
- throw new RuntimeException(e.getMessage() + " in " + layoutSetName, e);
- } finally {
- parser.close();
- }
- // If the tag is not found, then the default script is Latin.
- return ScriptUtils.SCRIPT_LATIN;
- }
-
- private static int readScriptIdFromTagFeature(final Resources resources,
- final XmlPullParser parser) throws IOException, XmlPullParserException {
- final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.KeyboardLayoutSet_Feature);
- try {
- final int scriptId =
- featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript,
- ScriptUtils.SCRIPT_UNKNOWN);
- XmlParseUtils.checkEndTag(TAG_FEATURE, parser);
- return scriptId;
- } finally {
- featureAttr.recycle();
- }
- }
-
- public KeyboardLayoutSet build() {
- if (mParams.mSubtype == null)
- throw new RuntimeException("KeyboardLayoutSet subtype is not specified");
- final int xmlId = getXmlId(mResources, mParams.mKeyboardLayoutSetName);
- try {
- parseKeyboardLayoutSet(mResources, xmlId);
- } catch (final IOException | XmlPullParserException e) {
- throw new RuntimeException(e.getMessage() + " in " + mParams.mKeyboardLayoutSetName,
- e);
- }
- return new KeyboardLayoutSet(mContext, mParams);
- }
-
- private static int getXmlId(final Resources resources, final String keyboardLayoutSetName) {
- final String packageName = resources.getResourcePackageName(
- R.xml.keyboard_layout_set_qwerty);
- return resources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
- }
-
- private void parseKeyboardLayoutSet(final Resources res, final int resId)
- throws XmlPullParserException, IOException {
- final XmlResourceParser parser = res.getXml(resId);
- try {
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_KEYBOARD_SET.equals(tag)) {
- parseKeyboardLayoutSetContent(parser);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
- }
- }
- }
- } finally {
- parser.close();
- }
- }
-
- private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
- throws XmlPullParserException, IOException {
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_ELEMENT.equals(tag)) {
- parseKeyboardLayoutSetElement(parser);
- } else if (TAG_FEATURE.equals(tag)) {
- mParams.mScriptId = readScriptIdFromTagFeature(mResources, parser);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (TAG_KEYBOARD_SET.equals(tag)) {
- break;
- }
- throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_KEYBOARD_SET);
- }
- }
- }
-
- private void parseKeyboardLayoutSetElement(final XmlPullParser parser)
- throws XmlPullParserException, IOException {
- final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.KeyboardLayoutSet_Element);
- try {
- XmlParseUtils.checkAttributeExists(a,
- R.styleable.KeyboardLayoutSet_Element_elementName, "elementName",
- TAG_ELEMENT, parser);
- XmlParseUtils.checkAttributeExists(a,
- R.styleable.KeyboardLayoutSet_Element_elementKeyboard, "elementKeyboard",
- TAG_ELEMENT, parser);
- XmlParseUtils.checkEndTag(TAG_ELEMENT, parser);
-
- final ElementParams elementParams = new ElementParams();
- final int elementName = a.getInt(
- R.styleable.KeyboardLayoutSet_Element_elementName, 0);
- elementParams.mKeyboardXmlId = a.getResourceId(
- R.styleable.KeyboardLayoutSet_Element_elementKeyboard, 0);
- elementParams.mProximityCharsCorrectionEnabled = a.getBoolean(
- R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection,
- false);
- elementParams.mSupportsSplitLayout = a.getBoolean(
- R.styleable.KeyboardLayoutSet_Element_supportsSplitLayout, false);
- elementParams.mAllowRedundantMoreKeys = a.getBoolean(
- R.styleable.KeyboardLayoutSet_Element_allowRedundantMoreKeys, true);
- mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams);
- } finally {
- a.recycle();
- }
- }
-
- private static int getKeyboardMode(final EditorInfo editorInfo) {
- final int inputType = editorInfo.inputType;
- final int variation = inputType & InputType.TYPE_MASK_VARIATION;
-
- switch (inputType & InputType.TYPE_MASK_CLASS) {
- case InputType.TYPE_CLASS_NUMBER:
- return KeyboardId.MODE_NUMBER;
- case InputType.TYPE_CLASS_DATETIME:
- switch (variation) {
- case InputType.TYPE_DATETIME_VARIATION_DATE:
- return KeyboardId.MODE_DATE;
- case InputType.TYPE_DATETIME_VARIATION_TIME:
- return KeyboardId.MODE_TIME;
- default: // InputType.TYPE_DATETIME_VARIATION_NORMAL
- return KeyboardId.MODE_DATETIME;
- }
- case InputType.TYPE_CLASS_PHONE:
- return KeyboardId.MODE_PHONE;
- case InputType.TYPE_CLASS_TEXT:
- if (InputTypeUtils.isEmailVariation(variation)) {
- return KeyboardId.MODE_EMAIL;
- } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
- return KeyboardId.MODE_URL;
- } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
- return KeyboardId.MODE_IM;
- } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
- return KeyboardId.MODE_TEXT;
- } else {
- return KeyboardId.MODE_TEXT;
- }
- default:
- return KeyboardId.MODE_TEXT;
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
deleted file mode 100644
index d398ab8c5..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-
-import androidx.annotation.NonNull;
-
-import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
-import com.android.inputmethod.event.Event;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
-import com.android.inputmethod.keyboard.emoji.EmojiPalettesView;
-import com.android.inputmethod.keyboard.internal.KeyboardState;
-import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
-import com.android.inputmethod.latin.InputView;
-import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.utils.CapsModeUtils;
-import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils;
-import com.android.inputmethod.latin.utils.RecapitalizeStatus;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.ScriptUtils;
-
-import javax.annotation.Nonnull;
-
-public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
- private static final String TAG = KeyboardSwitcher.class.getSimpleName();
-
- private InputView mCurrentInputView;
- private View mMainKeyboardFrame;
- private MainKeyboardView mKeyboardView;
- private EmojiPalettesView mEmojiPalettesView;
- private LatinIME mLatinIME;
- private RichInputMethodManager mRichImm;
- private boolean mIsHardwareAcceleratedDrawingEnabled;
-
- private KeyboardState mState;
-
- private KeyboardLayoutSet mKeyboardLayoutSet;
- // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}.
- private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet();
-
- private KeyboardTheme mKeyboardTheme;
- private Context mThemeContext;
-
- private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
-
- public static KeyboardSwitcher getInstance() {
- return sInstance;
- }
-
- private KeyboardSwitcher() {
- // Intentional empty constructor for singleton.
- }
-
- public static void init(final LatinIME latinIme) {
- sInstance.initInternal(latinIme);
- }
-
- private void initInternal(final LatinIME latinIme) {
- mLatinIME = latinIme;
- mRichImm = RichInputMethodManager.getInstance();
- mState = new KeyboardState(this);
- mIsHardwareAcceleratedDrawingEnabled =
- InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME);
- }
-
- public void updateKeyboardTheme(@NonNull Context displayContext) {
- final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper(
- displayContext, KeyboardTheme.getKeyboardTheme(displayContext /* context */));
- if (themeUpdated && mKeyboardView != null) {
- mLatinIME.setInputView(
- onCreateInputView(displayContext, mIsHardwareAcceleratedDrawingEnabled));
- }
- }
-
- private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
- final KeyboardTheme keyboardTheme) {
- if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)
- || !mThemeContext.getResources().equals(context.getResources())) {
- mKeyboardTheme = keyboardTheme;
- mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
- KeyboardLayoutSet.onKeyboardThemeChanged();
- return true;
- }
- return false;
- }
-
- public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
- final int currentAutoCapsState, final int currentRecapitalizeState) {
- final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
- mThemeContext, editorInfo);
- final Resources res = mThemeContext.getResources();
- final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(mThemeContext);
- final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
- builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
- builder.setSubtype(mRichImm.getCurrentSubtype());
- builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
- builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
- builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
- && settingsValues.mIsSplitKeyboardEnabled);
- mKeyboardLayoutSet = builder.build();
- try {
- mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
- mKeyboardTextsSet.setLocale(mRichImm.getCurrentSubtypeLocale(), mThemeContext);
- } catch (KeyboardLayoutSetException e) {
- Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
- }
- }
-
- public void saveKeyboardState() {
- if (getKeyboard() != null || isShowingEmojiPalettes()) {
- mState.onSaveKeyboardState();
- }
- }
-
- public void onHideWindow() {
- if (mKeyboardView != null) {
- mKeyboardView.onHideWindow();
- }
- }
-
- private void setKeyboard(
- @Nonnull final int keyboardId,
- @Nonnull final KeyboardSwitchState toggleState) {
- // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
- final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
- setMainKeyboardFrame(currentSettingsValues, toggleState);
- // TODO: pass this object to setKeyboard instead of getting the current values.
- final MainKeyboardView keyboardView = mKeyboardView;
- final Keyboard oldKeyboard = keyboardView.getKeyboard();
- final Keyboard newKeyboard = mKeyboardLayoutSet.getKeyboard(keyboardId);
- keyboardView.setKeyboard(newKeyboard);
- mCurrentInputView.setKeyboardTopPadding(newKeyboard.mTopPadding);
- keyboardView.setKeyPreviewPopupEnabled(
- currentSettingsValues.mKeyPreviewPopupOn,
- currentSettingsValues.mKeyPreviewPopupDismissDelay);
- keyboardView.setKeyPreviewAnimationParams(
- currentSettingsValues.mHasCustomKeyPreviewAnimationParams,
- currentSettingsValues.mKeyPreviewShowUpStartXScale,
- currentSettingsValues.mKeyPreviewShowUpStartYScale,
- currentSettingsValues.mKeyPreviewShowUpDuration,
- currentSettingsValues.mKeyPreviewDismissEndXScale,
- currentSettingsValues.mKeyPreviewDismissEndYScale,
- currentSettingsValues.mKeyPreviewDismissDuration);
- keyboardView.updateShortcutKey(mRichImm.isShortcutImeReady());
- final boolean subtypeChanged = (oldKeyboard == null)
- || !newKeyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
- final int languageOnSpacebarFormatType = LanguageOnSpacebarUtils
- .getLanguageOnSpacebarFormatType(newKeyboard.mId.mSubtype);
- final boolean hasMultipleEnabledIMEsOrSubtypes = mRichImm
- .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */);
- keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType,
- hasMultipleEnabledIMEsOrSubtypes);
- }
-
- public Keyboard getKeyboard() {
- if (mKeyboardView != null) {
- return mKeyboardView.getKeyboard();
- }
- return null;
- }
-
- // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
- // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
- public void resetKeyboardStateToAlphabet(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- mState.onResetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState);
- }
-
- public void onPressKey(final int code, final boolean isSinglePointer,
- final int currentAutoCapsState, final int currentRecapitalizeState) {
- mState.onPressKey(code, isSinglePointer, currentAutoCapsState, currentRecapitalizeState);
- }
-
- public void onReleaseKey(final int code, final boolean withSliding,
- final int currentAutoCapsState, final int currentRecapitalizeState) {
- mState.onReleaseKey(code, withSliding, currentAutoCapsState, currentRecapitalizeState);
- }
-
- public void onFinishSlidingInput(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- mState.onFinishSlidingInput(currentAutoCapsState, currentRecapitalizeState);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setAlphabetKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setAlphabetKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_ALPHABET, KeyboardSwitchState.OTHER);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setAlphabetManualShiftedKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setAlphabetManualShiftedKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardSwitchState.OTHER);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setAlphabetAutomaticShiftedKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardSwitchState.OTHER);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setAlphabetShiftLockedKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setAlphabetShiftLockedKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardSwitchState.OTHER);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setAlphabetShiftLockShiftedKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED, KeyboardSwitchState.OTHER);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setSymbolsKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setSymbolsKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_SYMBOLS, KeyboardSwitchState.OTHER);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setSymbolsShiftedKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setSymbolsShiftedKeyboard");
- }
- setKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED, KeyboardSwitchState.SYMBOLS_SHIFTED);
- }
-
- public boolean isImeSuppressedByHardwareKeyboard(
- @Nonnull final SettingsValues settingsValues,
- @Nonnull final KeyboardSwitchState toggleState) {
- return settingsValues.mHasHardwareKeyboard && toggleState == KeyboardSwitchState.HIDDEN;
- }
-
- private void setMainKeyboardFrame(
- @Nonnull final SettingsValues settingsValues,
- @Nonnull final KeyboardSwitchState toggleState) {
- final int visibility = isImeSuppressedByHardwareKeyboard(settingsValues, toggleState)
- ? View.GONE : View.VISIBLE;
- mKeyboardView.setVisibility(visibility);
- // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
- // @see #getVisibleKeyboardView() and
- // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
- mMainKeyboardFrame.setVisibility(visibility);
- mEmojiPalettesView.setVisibility(View.GONE);
- mEmojiPalettesView.stopEmojiPalettes();
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void setEmojiKeyboard() {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setEmojiKeyboard");
- }
- final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
- mMainKeyboardFrame.setVisibility(View.GONE);
- // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
- // @see #getVisibleKeyboardView() and
- // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
- mKeyboardView.setVisibility(View.GONE);
- mEmojiPalettesView.startEmojiPalettes(
- mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
- mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
- mEmojiPalettesView.setVisibility(View.VISIBLE);
- }
-
- public enum KeyboardSwitchState {
- HIDDEN(-1),
- SYMBOLS_SHIFTED(KeyboardId.ELEMENT_SYMBOLS_SHIFTED),
- EMOJI(KeyboardId.ELEMENT_EMOJI_RECENTS),
- OTHER(-1);
-
- final int mKeyboardId;
-
- KeyboardSwitchState(int keyboardId) {
- mKeyboardId = keyboardId;
- }
- }
-
- public KeyboardSwitchState getKeyboardSwitchState() {
- boolean hidden = !isShowingEmojiPalettes()
- && (mKeyboardLayoutSet == null
- || mKeyboardView == null
- || !mKeyboardView.isShown());
- KeyboardSwitchState state;
- if (hidden) {
- return KeyboardSwitchState.HIDDEN;
- } else if (isShowingEmojiPalettes()) {
- return KeyboardSwitchState.EMOJI;
- } else if (isShowingKeyboardId(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)) {
- return KeyboardSwitchState.SYMBOLS_SHIFTED;
- }
- return KeyboardSwitchState.OTHER;
- }
-
- public void onToggleKeyboard(@Nonnull final KeyboardSwitchState toggleState) {
- KeyboardSwitchState currentState = getKeyboardSwitchState();
- Log.w(TAG, "onToggleKeyboard() : Current = " + currentState + " : Toggle = " + toggleState);
- if (currentState == toggleState) {
- mLatinIME.stopShowingInputView();
- mLatinIME.hideWindow();
- setAlphabetKeyboard();
- } else {
- mLatinIME.startShowingInputView(true);
- if (toggleState == KeyboardSwitchState.EMOJI) {
- setEmojiKeyboard();
- } else {
- mEmojiPalettesView.stopEmojiPalettes();
- mEmojiPalettesView.setVisibility(View.GONE);
-
- mMainKeyboardFrame.setVisibility(View.VISIBLE);
- mKeyboardView.setVisibility(View.VISIBLE);
- setKeyboard(toggleState.mKeyboardId, toggleState);
- }
- }
- }
-
- // Future method for requesting an updating to the shift state.
- @Override
- public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_ACTION) {
- Log.d(TAG, "requestUpdatingShiftState: "
- + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags)
- + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode));
- }
- mState.onUpdateShiftState(autoCapsFlags, recapitalizeMode);
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void startDoubleTapShiftKeyTimer() {
- if (DEBUG_TIMER_ACTION) {
- Log.d(TAG, "startDoubleTapShiftKeyTimer");
- }
- final MainKeyboardView keyboardView = getMainKeyboardView();
- if (keyboardView != null) {
- keyboardView.startDoubleTapShiftKeyTimer();
- }
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public void cancelDoubleTapShiftKeyTimer() {
- if (DEBUG_TIMER_ACTION) {
- Log.d(TAG, "setAlphabetKeyboard");
- }
- final MainKeyboardView keyboardView = getMainKeyboardView();
- if (keyboardView != null) {
- keyboardView.cancelDoubleTapShiftKeyTimer();
- }
- }
-
- // Implements {@link KeyboardState.SwitchActions}.
- @Override
- public boolean isInDoubleTapShiftKeyTimeout() {
- if (DEBUG_TIMER_ACTION) {
- Log.d(TAG, "isInDoubleTapShiftKeyTimeout");
- }
- final MainKeyboardView keyboardView = getMainKeyboardView();
- return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
- }
-
- /**
- * Updates state machine to figure out when to automatically switch back to the previous mode.
- */
- public void onEvent(final Event event, final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- mState.onEvent(event, currentAutoCapsState, currentRecapitalizeState);
- }
-
- public boolean isShowingKeyboardId(@Nonnull int... keyboardIds) {
- if (mKeyboardView == null || !mKeyboardView.isShown()) {
- return false;
- }
- int activeKeyboardId = mKeyboardView.getKeyboard().mId.mElementId;
- for (int keyboardId : keyboardIds) {
- if (activeKeyboardId == keyboardId) {
- return true;
- }
- }
- return false;
- }
-
- public boolean isShowingEmojiPalettes() {
- return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
- }
-
- public boolean isShowingMoreKeysPanel() {
- if (isShowingEmojiPalettes()) {
- return false;
- }
- return mKeyboardView.isShowingMoreKeysPanel();
- }
-
- public View getVisibleKeyboardView() {
- if (isShowingEmojiPalettes()) {
- return mEmojiPalettesView;
- }
- return mKeyboardView;
- }
-
- public MainKeyboardView getMainKeyboardView() {
- return mKeyboardView;
- }
-
- public void deallocateMemory() {
- if (mKeyboardView != null) {
- mKeyboardView.cancelAllOngoingEvents();
- mKeyboardView.deallocateMemory();
- }
- if (mEmojiPalettesView != null) {
- mEmojiPalettesView.stopEmojiPalettes();
- }
- }
-
- public View onCreateInputView(@NonNull Context displayContext,
- final boolean isHardwareAcceleratedDrawingEnabled) {
- if (mKeyboardView != null) {
- mKeyboardView.closing();
- }
-
- updateKeyboardThemeAndContextThemeWrapper(
- displayContext, KeyboardTheme.getKeyboardTheme(displayContext /* context */));
- mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
- R.layout.input_view, null);
- mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
- mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
- R.id.emoji_palettes_view);
-
- mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
- mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
- mKeyboardView.setKeyboardActionListener(mLatinIME);
- mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
- isHardwareAcceleratedDrawingEnabled);
- mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
- return mCurrentInputView;
- }
-
- public int getKeyboardShiftMode() {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return WordComposer.CAPS_MODE_OFF;
- }
- switch (keyboard.mId.mElementId) {
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
- case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
- return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
- case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
- return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
- case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
- return WordComposer.CAPS_MODE_AUTO_SHIFTED;
- default:
- return WordComposer.CAPS_MODE_OFF;
- }
- }
-
- public int getCurrentKeyboardScriptId() {
- if (null == mKeyboardLayoutSet) {
- return ScriptUtils.SCRIPT_UNKNOWN;
- }
- return mKeyboardLayoutSet.getScriptId();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
deleted file mode 100644
index 006d08696..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-import com.android.inputmethod.compat.BuildCompatUtils;
-import com.android.inputmethod.latin.R;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-public final class KeyboardTheme implements Comparable<KeyboardTheme> {
- private static final String TAG = KeyboardTheme.class.getSimpleName();
-
- static final String KLP_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916";
- static final String LXX_KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509";
-
- // These should be aligned with Keyboard.themeId and Keyboard.Case.keyboardTheme
- // attributes' values in attrs.xml.
- public static final int THEME_ID_ICS = 0;
- public static final int THEME_ID_KLP = 2;
- public static final int THEME_ID_LXX_LIGHT = 3;
- public static final int THEME_ID_LXX_DARK = 4;
- public static final int DEFAULT_THEME_ID = THEME_ID_KLP;
-
- private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES;
-
- /* package private for testing */
- static final KeyboardTheme[] KEYBOARD_THEMES = {
- new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS,
- // This has never been selected because we support ICS or later.
- VERSION_CODES.BASE),
- new KeyboardTheme(THEME_ID_KLP, "KLP", R.style.KeyboardTheme_KLP,
- // Default theme for ICS, JB, and KLP.
- VERSION_CODES.ICE_CREAM_SANDWICH),
- new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light,
- // Default theme for LXX.
- Build.VERSION_CODES.LOLLIPOP),
- new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark,
- // This has never been selected as default theme.
- VERSION_CODES.BASE),
- };
-
- static {
- // Sort {@link #KEYBOARD_THEME} by descending order of {@link #mMinApiVersion}.
- Arrays.sort(KEYBOARD_THEMES);
- }
-
- public final int mThemeId;
- public final int mStyleId;
- public final String mThemeName;
- public final int mMinApiVersion;
-
- // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
- // in values/themes-<style>.xml.
- private KeyboardTheme(final int themeId, final String themeName, final int styleId,
- final int minApiVersion) {
- mThemeId = themeId;
- mThemeName = themeName;
- mStyleId = styleId;
- mMinApiVersion = minApiVersion;
- }
-
- @Override
- public int compareTo(final KeyboardTheme rhs) {
- if (mMinApiVersion > rhs.mMinApiVersion) return -1;
- if (mMinApiVersion < rhs.mMinApiVersion) return 1;
- return 0;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (o == this) return true;
- return (o instanceof KeyboardTheme) && ((KeyboardTheme)o).mThemeId == mThemeId;
- }
-
- @Override
- public int hashCode() {
- return mThemeId;
- }
-
- /* package private for testing */
- static KeyboardTheme searchKeyboardThemeById(final int themeId,
- final KeyboardTheme[] availableThemeIds) {
- // TODO: This search algorithm isn't optimal if there are many themes.
- for (final KeyboardTheme theme : availableThemeIds) {
- if (theme.mThemeId == themeId) {
- return theme;
- }
- }
- return null;
- }
-
- /* package private for testing */
- static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs,
- final int sdkVersion, final KeyboardTheme[] availableThemeArray) {
- final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null);
- if (klpThemeIdString != null) {
- if (sdkVersion <= VERSION_CODES.KITKAT) {
- try {
- final int themeId = Integer.parseInt(klpThemeIdString);
- final KeyboardTheme theme = searchKeyboardThemeById(themeId,
- availableThemeArray);
- if (theme != null) {
- return theme;
- }
- Log.w(TAG, "Unknown keyboard theme in KLP preference: " + klpThemeIdString);
- } catch (final NumberFormatException e) {
- Log.w(TAG, "Illegal keyboard theme in KLP preference: " + klpThemeIdString, e);
- }
- }
- // Remove old preference.
- Log.i(TAG, "Remove KLP keyboard theme preference: " + klpThemeIdString);
- prefs.edit().remove(KLP_KEYBOARD_THEME_KEY).apply();
- }
- // TODO: This search algorithm isn't optimal if there are many themes.
- for (final KeyboardTheme theme : availableThemeArray) {
- if (sdkVersion >= theme.mMinApiVersion) {
- return theme;
- }
- }
- return searchKeyboardThemeById(DEFAULT_THEME_ID, availableThemeArray);
- }
-
- public static String getKeyboardThemeName(final int themeId) {
- final KeyboardTheme theme = searchKeyboardThemeById(themeId, KEYBOARD_THEMES);
- return theme.mThemeName;
- }
-
- public static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs) {
- saveKeyboardThemeId(themeId, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT);
- }
-
- /* package private for testing */
- static String getPreferenceKey(final int sdkVersion) {
- if (sdkVersion <= VERSION_CODES.KITKAT) {
- return KLP_KEYBOARD_THEME_KEY;
- }
- return LXX_KEYBOARD_THEME_KEY;
- }
-
- /* package private for testing */
- static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs,
- final int sdkVersion) {
- final String prefKey = getPreferenceKey(sdkVersion);
- prefs.edit().putString(prefKey, Integer.toString(themeId)).apply();
- }
-
- public static KeyboardTheme getKeyboardTheme(final Context context) {
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- final KeyboardTheme[] availableThemeArray = getAvailableThemeArray(context);
- return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray);
- }
-
- /* package private for testing */
- static KeyboardTheme[] getAvailableThemeArray(final Context context) {
- if (AVAILABLE_KEYBOARD_THEMES == null) {
- final int[] availableThemeIdStringArray = context.getResources().getIntArray(
- R.array.keyboard_theme_ids);
- final ArrayList<KeyboardTheme> availableThemeList = new ArrayList<>();
- for (final int id : availableThemeIdStringArray) {
- final KeyboardTheme theme = searchKeyboardThemeById(id, KEYBOARD_THEMES);
- if (theme != null) {
- availableThemeList.add(theme);
- }
- }
- AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray(
- new KeyboardTheme[availableThemeList.size()]);
- Arrays.sort(AVAILABLE_KEYBOARD_THEMES);
- }
- return AVAILABLE_KEYBOARD_THEMES;
- }
-
- /* package private for testing */
- static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion,
- final KeyboardTheme[] availableThemeArray) {
- final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null);
- if (lxxThemeIdString == null) {
- return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray);
- }
- try {
- final int themeId = Integer.parseInt(lxxThemeIdString);
- final KeyboardTheme theme = searchKeyboardThemeById(themeId, availableThemeArray);
- if (theme != null) {
- return theme;
- }
- Log.w(TAG, "Unknown keyboard theme in LXX preference: " + lxxThemeIdString);
- } catch (final NumberFormatException e) {
- Log.w(TAG, "Illegal keyboard theme in LXX preference: " + lxxThemeIdString, e);
- }
- // Remove preference that contains unknown or illegal theme id.
- prefs.edit().remove(LXX_KEYBOARD_THEME_KEY).apply();
- return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
deleted file mode 100644
index a42108477..000000000
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.NinePatchDrawable;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.inputmethod.keyboard.internal.KeyDrawParams;
-import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.TypefaceUtils;
-
-import java.util.HashSet;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * A view that renders a virtual {@link Keyboard}.
- *
- * @attr ref android.R.styleable#KeyboardView_keyBackground
- * @attr ref android.R.styleable#KeyboardView_functionalKeyBackground
- * @attr ref android.R.styleable#KeyboardView_spacebarBackground
- * @attr ref android.R.styleable#KeyboardView_spacebarIconWidthRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyLabelFlags
- * @attr ref android.R.styleable#KeyboardView_keyHintLetterPadding
- * @attr ref android.R.styleable#KeyboardView_keyPopupHintLetter
- * @attr ref android.R.styleable#KeyboardView_keyPopupHintLetterPadding
- * @attr ref android.R.styleable#KeyboardView_keyShiftedLetterHintPadding
- * @attr ref android.R.styleable#KeyboardView_keyTextShadowRadius
- * @attr ref android.R.styleable#KeyboardView_verticalCorrection
- * @attr ref android.R.styleable#Keyboard_Key_keyTypeface
- * @attr ref android.R.styleable#Keyboard_Key_keyLetterSize
- * @attr ref android.R.styleable#Keyboard_Key_keyLabelSize
- * @attr ref android.R.styleable#Keyboard_Key_keyLargeLetterRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyLargeLabelRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyHintLetterRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyShiftedLetterHintRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyHintLabelRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyLabelOffCenterRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyHintLabelOffCenterRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyPreviewTextRatio
- * @attr ref android.R.styleable#Keyboard_Key_keyTextColor
- * @attr ref android.R.styleable#Keyboard_Key_keyTextColorDisabled
- * @attr ref android.R.styleable#Keyboard_Key_keyTextShadowColor
- * @attr ref android.R.styleable#Keyboard_Key_keyHintLetterColor
- * @attr ref android.R.styleable#Keyboard_Key_keyHintLabelColor
- * @attr ref android.R.styleable#Keyboard_Key_keyShiftedLetterHintInactivatedColor
- * @attr ref android.R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor
- * @attr ref android.R.styleable#Keyboard_Key_keyPreviewTextColor
- */
-public class KeyboardView extends View {
- // XML attributes
- private final KeyVisualAttributes mKeyVisualAttributes;
- // Default keyLabelFlags from {@link KeyboardTheme}.
- // Currently only "alignHintLabelToBottom" is supported.
- private final int mDefaultKeyLabelFlags;
- private final float mKeyHintLetterPadding;
- private final String mKeyPopupHintLetter;
- private final float mKeyPopupHintLetterPadding;
- private final float mKeyShiftedLetterHintPadding;
- private final float mKeyTextShadowRadius;
- private final float mVerticalCorrection;
- private final Drawable mKeyBackground;
- private final Drawable mFunctionalKeyBackground;
- private final Drawable mSpacebarBackground;
- private final float mSpacebarIconWidthRatio;
- private final Rect mKeyBackgroundPadding = new Rect();
- private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
-
- // The maximum key label width in the proportion to the key width.
- private static final float MAX_LABEL_RATIO = 0.90f;
-
- // Main keyboard
- // TODO: Consider having a base keyboard object to make this @Nonnull
- @Nullable
- private Keyboard mKeyboard;
- @Nonnull
- private final KeyDrawParams mKeyDrawParams = new KeyDrawParams();
-
- // Drawing
- /** True if all keys should be drawn */
- private boolean mInvalidateAllKeys;
- /** The keys that should be drawn */
- private final HashSet<Key> mInvalidatedKeys = new HashSet<>();
- /** The working rectangle for clipping */
- private final Rect mClipRect = new Rect();
- /** The keyboard bitmap buffer for faster updates */
- private Bitmap mOffscreenBuffer;
- /** The canvas for the above mutable keyboard bitmap */
- @Nonnull
- private final Canvas mOffscreenCanvas = new Canvas();
- @Nonnull
- private final Paint mPaint = new Paint();
- private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
-
- public KeyboardView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.keyboardViewStyle);
- }
-
- public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
-
- final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
- R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
- mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
- mKeyBackground.getPadding(mKeyBackgroundPadding);
- final Drawable functionalKeyBackground = keyboardViewAttr.getDrawable(
- R.styleable.KeyboardView_functionalKeyBackground);
- mFunctionalKeyBackground = (functionalKeyBackground != null) ? functionalKeyBackground
- : mKeyBackground;
- final Drawable spacebarBackground = keyboardViewAttr.getDrawable(
- R.styleable.KeyboardView_spacebarBackground);
- mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground;
- mSpacebarIconWidthRatio = keyboardViewAttr.getFloat(
- R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f);
- mKeyHintLetterPadding = keyboardViewAttr.getDimension(
- R.styleable.KeyboardView_keyHintLetterPadding, 0.0f);
- mKeyPopupHintLetter = keyboardViewAttr.getString(
- R.styleable.KeyboardView_keyPopupHintLetter);
- mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension(
- R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f);
- mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension(
- R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0.0f);
- mKeyTextShadowRadius = keyboardViewAttr.getFloat(
- R.styleable.KeyboardView_keyTextShadowRadius, KET_TEXT_SHADOW_RADIUS_DISABLED);
- mVerticalCorrection = keyboardViewAttr.getDimension(
- R.styleable.KeyboardView_verticalCorrection, 0.0f);
- keyboardViewAttr.recycle();
-
- final TypedArray keyAttr = context.obtainStyledAttributes(attrs,
- R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
- mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
- mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
- keyAttr.recycle();
-
- mPaint.setAntiAlias(true);
- }
-
- @Nullable
- public KeyVisualAttributes getKeyVisualAttribute() {
- return mKeyVisualAttributes;
- }
-
- private static void blendAlpha(@Nonnull final Paint paint, final int alpha) {
- final int color = paint.getColor();
- paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE,
- Color.red(color), Color.green(color), Color.blue(color));
- }
-
- public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
- if (!enabled) return;
- // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
- setLayerType(LAYER_TYPE_HARDWARE, null);
- }
-
- /**
- * Attaches a keyboard to this view. The keyboard can be switched at any time and the
- * view will re-layout itself to accommodate the keyboard.
- * @see Keyboard
- * @see #getKeyboard()
- * @param keyboard the keyboard to display in this view
- */
- public void setKeyboard(@Nonnull final Keyboard keyboard) {
- mKeyboard = keyboard;
- final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
- mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
- mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes);
- invalidateAllKeys();
- requestLayout();
- }
-
- /**
- * Returns the current keyboard being displayed by this view.
- * @return the currently attached keyboard
- * @see #setKeyboard(Keyboard)
- */
- @Nullable
- public Keyboard getKeyboard() {
- return mKeyboard;
- }
-
- protected float getVerticalCorrection() {
- return mVerticalCorrection;
- }
-
- @Nonnull
- protected KeyDrawParams getKeyDrawParams() {
- return mKeyDrawParams;
- }
-
- protected void updateKeyDrawParams(final int keyHeight) {
- mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
- }
-
- @Override
- protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
- // The main keyboard expands to the entire this {@link KeyboardView}.
- final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
- final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onDraw(final Canvas canvas) {
- super.onDraw(canvas);
- if (canvas.isHardwareAccelerated()) {
- onDrawKeyboard(canvas);
- return;
- }
-
- final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty();
- if (bufferNeedsUpdates || mOffscreenBuffer == null) {
- if (maybeAllocateOffscreenBuffer()) {
- mInvalidateAllKeys = true;
- // TODO: Stop using the offscreen canvas even when in software rendering
- mOffscreenCanvas.setBitmap(mOffscreenBuffer);
- }
- onDrawKeyboard(mOffscreenCanvas);
- }
- canvas.drawBitmap(mOffscreenBuffer, 0.0f, 0.0f, null);
- }
-
- private boolean maybeAllocateOffscreenBuffer() {
- final int width = getWidth();
- final int height = getHeight();
- if (width == 0 || height == 0) {
- return false;
- }
- if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width
- && mOffscreenBuffer.getHeight() == height) {
- return false;
- }
- freeOffscreenBuffer();
- mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- return true;
- }
-
- private void freeOffscreenBuffer() {
- mOffscreenCanvas.setBitmap(null);
- mOffscreenCanvas.setMatrix(null);
- if (mOffscreenBuffer != null) {
- mOffscreenBuffer.recycle();
- mOffscreenBuffer = null;
- }
- }
-
- private void onDrawKeyboard(@Nonnull final Canvas canvas) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return;
- }
-
- final Paint paint = mPaint;
- final Drawable background = getBackground();
- // Calculate clip region and set.
- final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty();
- final boolean isHardwareAccelerated = canvas.isHardwareAccelerated();
- // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
- if (drawAllKeys || isHardwareAccelerated) {
- if (!isHardwareAccelerated && background != null) {
- // Need to draw keyboard background on {@link #mOffscreenBuffer}.
- canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- background.draw(canvas);
- }
- // Draw all keys.
- for (final Key key : keyboard.getSortedKeys()) {
- onDrawKey(key, canvas, paint);
- }
- } else {
- for (final Key key : mInvalidatedKeys) {
- if (!keyboard.hasKey(key)) {
- continue;
- }
- if (background != null) {
- // Need to redraw key's background on {@link #mOffscreenBuffer}.
- final int x = key.getX() + getPaddingLeft();
- final int y = key.getY() + getPaddingTop();
- mClipRect.set(x, y, x + key.getWidth(), y + key.getHeight());
- canvas.save();
- canvas.clipRect(mClipRect);
- canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- background.draw(canvas);
- canvas.restore();
- }
- onDrawKey(key, canvas, paint);
- }
- }
-
- mInvalidatedKeys.clear();
- mInvalidateAllKeys = false;
- }
-
- private void onDrawKey(@Nonnull final Key key, @Nonnull final Canvas canvas,
- @Nonnull final Paint paint) {
- final int keyDrawX = key.getDrawX() + getPaddingLeft();
- final int keyDrawY = key.getY() + getPaddingTop();
- canvas.translate(keyDrawX, keyDrawY);
-
- final KeyVisualAttributes attr = key.getVisualAttributes();
- final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(key.getHeight(), attr);
- params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
-
- if (!key.isSpacer()) {
- final Drawable background = key.selectBackgroundDrawable(
- mKeyBackground, mFunctionalKeyBackground, mSpacebarBackground);
- if (background != null) {
- onDrawKeyBackground(key, canvas, background);
- }
- }
- onDrawKeyTopVisuals(key, canvas, paint, params);
-
- canvas.translate(-keyDrawX, -keyDrawY);
- }
-
- // Draw key background.
- protected void onDrawKeyBackground(@Nonnull final Key key, @Nonnull final Canvas canvas,
- @Nonnull final Drawable background) {
- final int keyWidth = key.getDrawWidth();
- final int keyHeight = key.getHeight();
- final int bgWidth, bgHeight, bgX, bgY;
- if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)
- // HACK: To disable expanding normal/functional key background.
- && !key.hasCustomActionLabel()) {
- final int intrinsicWidth = background.getIntrinsicWidth();
- final int intrinsicHeight = background.getIntrinsicHeight();
- final float minScale = Math.min(
- keyWidth / (float)intrinsicWidth, keyHeight / (float)intrinsicHeight);
- bgWidth = (int)(intrinsicWidth * minScale);
- bgHeight = (int)(intrinsicHeight * minScale);
- bgX = (keyWidth - bgWidth) / 2;
- bgY = (keyHeight - bgHeight) / 2;
- } else {
- final Rect padding = mKeyBackgroundPadding;
- bgWidth = keyWidth + padding.left + padding.right;
- bgHeight = keyHeight + padding.top + padding.bottom;
- bgX = -padding.left;
- bgY = -padding.top;
- }
- final Rect bounds = background.getBounds();
- if (bgWidth != bounds.right || bgHeight != bounds.bottom) {
- background.setBounds(0, 0, bgWidth, bgHeight);
- }
- canvas.translate(bgX, bgY);
- background.draw(canvas);
- canvas.translate(-bgX, -bgY);
- }
-
- // Draw key top visuals.
- protected void onDrawKeyTopVisuals(@Nonnull final Key key, @Nonnull final Canvas canvas,
- @Nonnull final Paint paint, @Nonnull final KeyDrawParams params) {
- final int keyWidth = key.getDrawWidth();
- final int keyHeight = key.getHeight();
- final float centerX = keyWidth * 0.5f;
- final float centerY = keyHeight * 0.5f;
-
- // Draw key label.
- final Keyboard keyboard = getKeyboard();
- final Drawable icon = (keyboard == null) ? null
- : key.getIcon(keyboard.mIconsSet, params.mAnimAlpha);
- float labelX = centerX;
- float labelBaseline = centerY;
- final String label = key.getLabel();
- if (label != null) {
- paint.setTypeface(key.selectTypeface(params));
- paint.setTextSize(key.selectTextSize(params));
- final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
- final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
-
- // Vertical label text alignment.
- labelBaseline = centerY + labelCharHeight / 2.0f;
-
- // Horizontal label text alignment
- if (key.isAlignLabelOffCenter()) {
- // The label is placed off center of the key. Used mainly on "phone number" layout.
- labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth;
- paint.setTextAlign(Align.LEFT);
- } else {
- labelX = centerX;
- paint.setTextAlign(Align.CENTER);
- }
- if (key.needsAutoXScale()) {
- final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) /
- TypefaceUtils.getStringWidth(label, paint));
- if (key.needsAutoScale()) {
- final float autoSize = paint.getTextSize() * ratio;
- paint.setTextSize(autoSize);
- } else {
- paint.setTextScaleX(ratio);
- }
- }
-
- if (key.isEnabled()) {
- paint.setColor(key.selectTextColor(params));
- // Set a drop shadow for the text if the shadow radius is positive value.
- if (mKeyTextShadowRadius > 0.0f) {
- paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor);
- } else {
- paint.clearShadowLayer();
- }
- } else {
- // Make label invisible
- paint.setColor(Color.TRANSPARENT);
- paint.clearShadowLayer();
- }
- blendAlpha(paint, params.mAnimAlpha);
- canvas.drawText(label, 0, label.length(), labelX, labelBaseline, paint);
- // Turn off drop shadow and reset x-scale.
- paint.clearShadowLayer();
- paint.setTextScaleX(1.0f);
- }
-
- // Draw hint label.
- final String hintLabel = key.getHintLabel();
- if (hintLabel != null) {
- paint.setTextSize(key.selectHintTextSize(params));
- paint.setColor(key.selectHintTextColor(params));
- // TODO: Should add a way to specify type face for hint letters
- paint.setTypeface(Typeface.DEFAULT_BOLD);
- blendAlpha(paint, params.mAnimAlpha);
- final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
- final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
- final float hintX, hintBaseline;
- if (key.hasHintLabel()) {
- // The hint label is placed just right of the key label. Used mainly on
- // "phone number" layout.
- hintX = labelX + params.mHintLabelOffCenterRatio * labelCharWidth;
- if (key.isAlignHintLabelToBottom(mDefaultKeyLabelFlags)) {
- hintBaseline = labelBaseline;
- } else {
- hintBaseline = centerY + labelCharHeight / 2.0f;
- }
- paint.setTextAlign(Align.LEFT);
- } else if (key.hasShiftedLetterHint()) {
- // The hint label is placed at top-right corner of the key. Used mainly on tablet.
- hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f;
- paint.getFontMetrics(mFontMetrics);
- hintBaseline = -mFontMetrics.top;
- paint.setTextAlign(Align.CENTER);
- } else { // key.hasHintLetter()
- // The hint letter is placed at top-right corner of the key. Used mainly on phone.
- final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint);
- final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint);
- hintX = keyWidth - mKeyHintLetterPadding
- - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f;
- hintBaseline = -paint.ascent();
- paint.setTextAlign(Align.CENTER);
- }
- final float adjustmentY = params.mHintLabelVerticalAdjustment * labelCharHeight;
- canvas.drawText(
- hintLabel, 0, hintLabel.length(), hintX, hintBaseline + adjustmentY, paint);
- }
-
- // Draw key icon.
- if (label == null && icon != null) {
- final int iconWidth;
- if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) {
- iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio);
- } else {
- iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth);
- }
- final int iconHeight = icon.getIntrinsicHeight();
- final int iconY;
- if (key.isAlignIconToBottom()) {
- iconY = keyHeight - iconHeight;
- } else {
- iconY = (keyHeight - iconHeight) / 2; // Align vertically center.
- }
- final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center.
- drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
- }
-
- if (key.hasPopupHint() && key.getMoreKeys() != null) {
- drawKeyPopupHint(key, canvas, paint, params);
- }
- }
-
- // Draw popup hint "..." at the bottom right corner of the key.
- protected void drawKeyPopupHint(@Nonnull final Key key, @Nonnull final Canvas canvas,
- @Nonnull final Paint paint, @Nonnull final KeyDrawParams params) {
- if (TextUtils.isEmpty(mKeyPopupHintLetter)) {
- return;
- }
- final int keyWidth = key.getDrawWidth();
- final int keyHeight = key.getHeight();
-
- paint.setTypeface(params.mTypeface);
- paint.setTextSize(params.mHintLetterSize);
- paint.setColor(params.mHintLabelColor);
- paint.setTextAlign(Align.CENTER);
- final float hintX = keyWidth - mKeyHintLetterPadding
- - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f;
- final float hintY = keyHeight - mKeyPopupHintLetterPadding;
- canvas.drawText(mKeyPopupHintLetter, hintX, hintY, paint);
- }
-
- protected static void drawIcon(@Nonnull final Canvas canvas,@Nonnull final Drawable icon,
- final int x, final int y, final int width, final int height) {
- canvas.translate(x, y);
- icon.setBounds(0, 0, width, height);
- icon.draw(canvas);
- canvas.translate(-x, -y);
- }
-
- public Paint newLabelPaint(@Nullable final Key key) {
- final Paint paint = new Paint();
- paint.setAntiAlias(true);
- if (key == null) {
- paint.setTypeface(mKeyDrawParams.mTypeface);
- paint.setTextSize(mKeyDrawParams.mLabelSize);
- } else {
- paint.setColor(key.selectTextColor(mKeyDrawParams));
- paint.setTypeface(key.selectTypeface(mKeyDrawParams));
- paint.setTextSize(key.selectTextSize(mKeyDrawParams));
- }
- return paint;
- }
-
- /**
- * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
- * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
- * draws the cached buffer.
- * @see #invalidateKey(Key)
- */
- public void invalidateAllKeys() {
- mInvalidatedKeys.clear();
- mInvalidateAllKeys = true;
- invalidate();
- }
-
- /**
- * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only
- * one key is changing it's content. Any changes that affect the position or size of the key
- * may not be honored.
- * @param key key in the attached {@link Keyboard}.
- * @see #invalidateAllKeys
- */
- public void invalidateKey(@Nullable final Key key) {
- if (mInvalidateAllKeys || key == null) {
- return;
- }
- mInvalidatedKeys.add(key);
- final int x = key.getX() + getPaddingLeft();
- final int y = key.getY() + getPaddingTop();
- invalidate(x, y, x + key.getWidth(), y + key.getHeight());
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- freeOffscreenBuffer();
- }
-
- public void deallocateMemory() {
- freeOffscreenBuffer();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
deleted file mode 100644
index fc8744ec6..000000000
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ /dev/null
@@ -1,895 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.animation.AnimatorInflater;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Typeface;
-import android.preference.PreferenceManager;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate;
-import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
-import com.android.inputmethod.keyboard.internal.DrawingProxy;
-import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
-import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview;
-import com.android.inputmethod.keyboard.internal.KeyDrawParams;
-import com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer;
-import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
-import com.android.inputmethod.keyboard.internal.KeyPreviewView;
-import com.android.inputmethod.keyboard.internal.MoreKeySpec;
-import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
-import com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview;
-import com.android.inputmethod.keyboard.internal.TimerHandler;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-import com.android.inputmethod.latin.settings.DebugSettings;
-import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils;
-import com.android.inputmethod.latin.utils.TypefaceUtils;
-
-import java.util.Locale;
-import java.util.WeakHashMap;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * A view that is responsible for detecting key presses and touch movements.
- *
- * @attr ref android.R.styleable#MainKeyboardView_languageOnSpacebarTextRatio
- * @attr ref android.R.styleable#MainKeyboardView_languageOnSpacebarTextColor
- * @attr ref android.R.styleable#MainKeyboardView_languageOnSpacebarTextShadowRadius
- * @attr ref android.R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor
- * @attr ref android.R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
- * @attr ref android.R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
- * @attr ref android.R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
- * @attr ref android.R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
- * @attr ref android.R.styleable#MainKeyboardView_keyHysteresisDistance
- * @attr ref android.R.styleable#MainKeyboardView_touchNoiseThresholdTime
- * @attr ref android.R.styleable#MainKeyboardView_touchNoiseThresholdDistance
- * @attr ref android.R.styleable#MainKeyboardView_keySelectionByDraggingFinger
- * @attr ref android.R.styleable#MainKeyboardView_keyRepeatStartTimeout
- * @attr ref android.R.styleable#MainKeyboardView_keyRepeatInterval
- * @attr ref android.R.styleable#MainKeyboardView_longPressKeyTimeout
- * @attr ref android.R.styleable#MainKeyboardView_longPressShiftKeyTimeout
- * @attr ref android.R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
- * @attr ref android.R.styleable#MainKeyboardView_keyPreviewLayout
- * @attr ref android.R.styleable#MainKeyboardView_keyPreviewOffset
- * @attr ref android.R.styleable#MainKeyboardView_keyPreviewHeight
- * @attr ref android.R.styleable#MainKeyboardView_keyPreviewLingerTimeout
- * @attr ref android.R.styleable#MainKeyboardView_keyPreviewShowUpAnimator
- * @attr ref android.R.styleable#MainKeyboardView_keyPreviewDismissAnimator
- * @attr ref android.R.styleable#MainKeyboardView_moreKeysKeyboardLayout
- * @attr ref android.R.styleable#MainKeyboardView_moreKeysKeyboardForActionLayout
- * @attr ref android.R.styleable#MainKeyboardView_backgroundDimAlpha
- * @attr ref android.R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
- * @attr ref android.R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout
- * @attr ref android.R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
- * @attr ref android.R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
- * @attr ref android.R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
- * @attr ref android.R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
- * @attr ref android.R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
- * @attr ref android.R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
- */
-public final class MainKeyboardView extends KeyboardView implements DrawingProxy,
- MoreKeysPanel.Controller {
- private static final String TAG = MainKeyboardView.class.getSimpleName();
-
- /** Listener for {@link KeyboardActionListener}. */
- private KeyboardActionListener mKeyboardActionListener;
-
- /* Space key and its icon and background. */
- private Key mSpaceKey;
- // Stuff to draw language name on spacebar.
- private final int mLanguageOnSpacebarFinalAlpha;
- private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
- private int mLanguageOnSpacebarFormatType;
- private boolean mHasMultipleEnabledIMEsOrSubtypes;
- private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
- private final float mLanguageOnSpacebarTextRatio;
- private float mLanguageOnSpacebarTextSize;
- private final int mLanguageOnSpacebarTextColor;
- private final float mLanguageOnSpacebarTextShadowRadius;
- private final int mLanguageOnSpacebarTextShadowColor;
- private static final float LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
- // The minimum x-scale to fit the language name on spacebar.
- private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
-
- // Stuff to draw altCodeWhileTyping keys.
- private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
- private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
- private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
-
- // Drawing preview placer view
- private final DrawingPreviewPlacerView mDrawingPreviewPlacerView;
- private final int[] mOriginCoords = CoordinateUtils.newInstance();
- private final GestureFloatingTextDrawingPreview mGestureFloatingTextDrawingPreview;
- private final GestureTrailsDrawingPreview mGestureTrailsDrawingPreview;
- private final SlidingKeyInputDrawingPreview mSlidingKeyInputDrawingPreview;
-
- // Key preview
- private final KeyPreviewDrawParams mKeyPreviewDrawParams;
- private final KeyPreviewChoreographer mKeyPreviewChoreographer;
-
- // More keys keyboard
- private final Paint mBackgroundDimAlphaPaint = new Paint();
- private final View mMoreKeysKeyboardContainer;
- private final View mMoreKeysKeyboardForActionContainer;
- private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>();
- private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
- // More keys panel (used by both more keys keyboard and more suggestions view)
- // TODO: Consider extending to support multiple more keys panels
- private MoreKeysPanel mMoreKeysPanel;
-
- // Gesture floating preview text
- // TODO: Make this parameter customizable by user via settings.
- private int mGestureFloatingPreviewTextLingerTimeout;
-
- private final KeyDetector mKeyDetector;
- private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
-
- private final TimerHandler mTimerHandler;
- private final int mLanguageOnSpacebarHorizontalMargin;
-
- private MainKeyboardAccessibilityDelegate mAccessibilityDelegate;
-
- public MainKeyboardView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.mainKeyboardViewStyle);
- }
-
- public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
-
- final DrawingPreviewPlacerView drawingPreviewPlacerView =
- new DrawingPreviewPlacerView(context, attrs);
-
- final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
- attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
- final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
- final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
- mTimerHandler = new TimerHandler(
- this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
-
- final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
- final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
- mKeyDetector = new KeyDetector(
- keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
-
- PointerTracker.init(mainKeyboardViewAttr, mTimerHandler, this /* DrawingProxy */);
-
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- final boolean forceNonDistinctMultitouch = prefs.getBoolean(
- DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
- final boolean hasDistinctMultitouch = context.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
- && !forceNonDistinctMultitouch;
- mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null
- : new NonDistinctMultitouchHelper();
-
- final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
- mBackgroundDimAlphaPaint.setColor(Color.BLACK);
- mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
- mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
- R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
- mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0);
- mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat(
- R.styleable.MainKeyboardView_languageOnSpacebarTextShadowRadius,
- LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED);
- mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0);
- mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
- Constants.Color.ALPHA_OPAQUE);
- final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
- final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
- final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
-
- mKeyPreviewDrawParams = new KeyPreviewDrawParams(mainKeyboardViewAttr);
- mKeyPreviewChoreographer = new KeyPreviewChoreographer(mKeyPreviewDrawParams);
-
- final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
- final int moreKeysKeyboardForActionLayoutId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_moreKeysKeyboardForActionLayout,
- moreKeysKeyboardLayoutId);
- mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
- R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
-
- mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
-
- mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview(
- mainKeyboardViewAttr);
- mGestureFloatingTextDrawingPreview.setDrawingView(drawingPreviewPlacerView);
-
- mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview(mainKeyboardViewAttr);
- mGestureTrailsDrawingPreview.setDrawingView(drawingPreviewPlacerView);
-
- mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview(mainKeyboardViewAttr);
- mSlidingKeyInputDrawingPreview.setDrawingView(drawingPreviewPlacerView);
- mainKeyboardViewAttr.recycle();
-
- mDrawingPreviewPlacerView = drawingPreviewPlacerView;
-
- final LayoutInflater inflater = LayoutInflater.from(getContext());
- mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null);
- mMoreKeysKeyboardForActionContainer = inflater.inflate(
- moreKeysKeyboardForActionLayoutId, null);
- mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
- languageOnSpacebarFadeoutAnimatorResId, this);
- mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
- altCodeKeyWhileTypingFadeoutAnimatorResId, this);
- mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
- altCodeKeyWhileTypingFadeinAnimatorResId, this);
-
- mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
-
- mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension(
- R.dimen.config_language_on_spacebar_horizontal_margin);
- }
-
- @Override
- public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
- super.setHardwareAcceleratedDrawingEnabled(enabled);
- mDrawingPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled);
- }
-
- private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
- if (resId == 0) {
- // TODO: Stop returning null.
- return null;
- }
- final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
- getContext(), resId);
- if (animator != null) {
- animator.setTarget(target);
- }
- return animator;
- }
-
- private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
- final ObjectAnimator animatorToStart) {
- if (animatorToCancel == null || animatorToStart == null) {
- // TODO: Stop using null as a no-operation animator.
- return;
- }
- float startFraction = 0.0f;
- if (animatorToCancel.isStarted()) {
- animatorToCancel.cancel();
- startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
- }
- final long startTime = (long)(animatorToStart.getDuration() * startFraction);
- animatorToStart.start();
- animatorToStart.setCurrentPlayTime(startTime);
- }
-
- // Implements {@link DrawingProxy#startWhileTypingAnimation(int)}.
- /**
- * Called when a while-typing-animation should be started.
- * @param fadeInOrOut {@link DrawingProxy#FADE_IN} starts while-typing-fade-in animation.
- * {@link DrawingProxy#FADE_OUT} starts while-typing-fade-out animation.
- */
- @Override
- public void startWhileTypingAnimation(final int fadeInOrOut) {
- switch (fadeInOrOut) {
- case DrawingProxy.FADE_IN:
- cancelAndStartAnimators(
- mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
- break;
- case DrawingProxy.FADE_OUT:
- cancelAndStartAnimators(
- mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
- break;
- }
- }
-
- @ExternallyReferenced
- public int getLanguageOnSpacebarAnimAlpha() {
- return mLanguageOnSpacebarAnimAlpha;
- }
-
- @ExternallyReferenced
- public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
- mLanguageOnSpacebarAnimAlpha = alpha;
- invalidateKey(mSpaceKey);
- }
-
- @ExternallyReferenced
- public int getAltCodeKeyWhileTypingAnimAlpha() {
- return mAltCodeKeyWhileTypingAnimAlpha;
- }
-
- @ExternallyReferenced
- public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
- if (mAltCodeKeyWhileTypingAnimAlpha == alpha) {
- return;
- }
- // Update the visual of alt-code-key-while-typing.
- mAltCodeKeyWhileTypingAnimAlpha = alpha;
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return;
- }
- for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
- invalidateKey(key);
- }
- }
-
- public void setKeyboardActionListener(final KeyboardActionListener listener) {
- mKeyboardActionListener = listener;
- PointerTracker.setKeyboardActionListener(listener);
- }
-
- // TODO: We should reconsider which coordinate system should be used to represent keyboard
- // event.
- public int getKeyX(final int x) {
- return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x;
- }
-
- // TODO: We should reconsider which coordinate system should be used to represent keyboard
- // event.
- public int getKeyY(final int y) {
- return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y;
- }
-
- /**
- * Attaches a keyboard to this view. The keyboard can be switched at any time and the
- * view will re-layout itself to accommodate the keyboard.
- * @see Keyboard
- * @see #getKeyboard()
- * @param keyboard the keyboard to display in this view
- */
- @Override
- public void setKeyboard(final Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview and key repeat.
- mTimerHandler.cancelLongPressTimers();
- super.setKeyboard(keyboard);
- mKeyDetector.setKeyboard(
- keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
- PointerTracker.setKeyDetector(mKeyDetector);
- mMoreKeysKeyboardCache.clear();
-
- mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
- final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
- mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio;
-
- if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- if (mAccessibilityDelegate == null) {
- mAccessibilityDelegate = new MainKeyboardAccessibilityDelegate(this, mKeyDetector);
- }
- mAccessibilityDelegate.setKeyboard(keyboard);
- } else {
- mAccessibilityDelegate = null;
- }
- }
-
- /**
- * Enables or disables the key preview popup. This is a popup that shows a magnified
- * version of the depressed key. By default the preview is enabled.
- * @param previewEnabled whether or not to enable the key feedback preview
- * @param delay the delay after which the preview is dismissed
- */
- public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
- mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay);
- }
-
- /**
- * Enables or disables the key preview popup animations and set animations' parameters.
- *
- * @param hasCustomAnimationParams false to use the default key preview popup animations
- * specified by keyPreviewShowUpAnimator and keyPreviewDismissAnimator attributes.
- * true to override the default animations with the specified parameters.
- * @param showUpStartXScale from this x-scale the show up animation will start.
- * @param showUpStartYScale from this y-scale the show up animation will start.
- * @param showUpDuration the duration of the show up animation in milliseconds.
- * @param dismissEndXScale to this x-scale the dismiss animation will end.
- * @param dismissEndYScale to this y-scale the dismiss animation will end.
- * @param dismissDuration the duration of the dismiss animation in milliseconds.
- */
- public void setKeyPreviewAnimationParams(final boolean hasCustomAnimationParams,
- final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration,
- final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) {
- mKeyPreviewDrawParams.setAnimationParams(hasCustomAnimationParams,
- showUpStartXScale, showUpStartYScale, showUpDuration,
- dismissEndXScale, dismissEndYScale, dismissDuration);
- }
-
- private void locatePreviewPlacerView() {
- getLocationInWindow(mOriginCoords);
- mDrawingPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, getWidth(), getHeight());
- }
-
- private void installPreviewPlacerView() {
- final View rootView = getRootView();
- if (rootView == null) {
- Log.w(TAG, "Cannot find root view");
- return;
- }
- final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
- // Note: It'd be very weird if we get null by android.R.id.content.
- if (windowContentView == null) {
- Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView");
- return;
- }
- windowContentView.addView(mDrawingPreviewPlacerView);
- }
-
- // Implements {@link DrawingProxy#onKeyPressed(Key,boolean)}.
- @Override
- public void onKeyPressed(@Nonnull final Key key, final boolean withPreview) {
- key.onPressed();
- invalidateKey(key);
- if (withPreview && !key.noKeyPreview()) {
- showKeyPreview(key);
- }
- }
-
- private void showKeyPreview(@Nonnull final Key key) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return;
- }
- final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
- if (!previewParams.isPopupEnabled()) {
- previewParams.setVisibleOffset(-keyboard.mVerticalGap);
- return;
- }
-
- locatePreviewPlacerView();
- getLocationInWindow(mOriginCoords);
- mKeyPreviewChoreographer.placeAndShowKeyPreview(key, keyboard.mIconsSet, getKeyDrawParams(),
- getWidth(), mOriginCoords, mDrawingPreviewPlacerView, isHardwareAccelerated());
- }
-
- private void dismissKeyPreviewWithoutDelay(@Nonnull final Key key) {
- mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
- invalidateKey(key);
- }
-
- // Implements {@link DrawingProxy#onKeyReleased(Key,boolean)}.
- @Override
- public void onKeyReleased(@Nonnull final Key key, final boolean withAnimation) {
- key.onReleased();
- invalidateKey(key);
- if (!key.noKeyPreview()) {
- if (withAnimation) {
- dismissKeyPreview(key);
- } else {
- dismissKeyPreviewWithoutDelay(key);
- }
- }
- }
-
- private void dismissKeyPreview(@Nonnull final Key key) {
- if (isHardwareAccelerated()) {
- mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */);
- return;
- }
- // TODO: Implement preference option to control key preview method and duration.
- mTimerHandler.postDismissKeyPreview(key, mKeyPreviewDrawParams.getLingerTimeout());
- }
-
- public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
- mSlidingKeyInputDrawingPreview.setPreviewEnabled(enabled);
- }
-
- @Override
- public void showSlidingKeyInputPreview(@Nullable final PointerTracker tracker) {
- locatePreviewPlacerView();
- if (tracker != null) {
- mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker);
- } else {
- mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
- }
- }
-
- private void setGesturePreviewMode(final boolean isGestureTrailEnabled,
- final boolean isGestureFloatingPreviewTextEnabled) {
- mGestureFloatingTextDrawingPreview.setPreviewEnabled(isGestureFloatingPreviewTextEnabled);
- mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled);
- }
-
- public void showGestureFloatingPreviewText(@Nonnull final SuggestedWords suggestedWords,
- final boolean dismissDelayed) {
- locatePreviewPlacerView();
- final GestureFloatingTextDrawingPreview gestureFloatingTextDrawingPreview =
- mGestureFloatingTextDrawingPreview;
- gestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords);
- if (dismissDelayed) {
- mTimerHandler.postDismissGestureFloatingPreviewText(
- mGestureFloatingPreviewTextLingerTimeout);
- }
- }
-
- // Implements {@link DrawingProxy#dismissGestureFloatingPreviewTextWithoutDelay()}.
- @Override
- public void dismissGestureFloatingPreviewTextWithoutDelay() {
- mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText();
- }
-
- @Override
- public void showGestureTrail(@Nonnull final PointerTracker tracker,
- final boolean showsFloatingPreviewText) {
- locatePreviewPlacerView();
- if (showsFloatingPreviewText) {
- mGestureFloatingTextDrawingPreview.setPreviewPosition(tracker);
- }
- mGestureTrailsDrawingPreview.setPreviewPosition(tracker);
- }
-
- // Note that this method is called from a non-UI thread.
- @SuppressWarnings("static-method")
- public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
- PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
- }
-
- public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser,
- final boolean isGestureTrailEnabled,
- final boolean isGestureFloatingPreviewTextEnabled) {
- PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser);
- setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled,
- isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- installPreviewPlacerView();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mDrawingPreviewPlacerView.removeAllViews();
- }
-
- // Implements {@link DrawingProxy@showMoreKeysKeyboard(Key,PointerTracker)}.
- @Override
- @Nullable
- public MoreKeysPanel showMoreKeysKeyboard(@Nonnull final Key key,
- @Nonnull final PointerTracker tracker) {
- final MoreKeySpec[] moreKeys = key.getMoreKeys();
- if (moreKeys == null) {
- return null;
- }
- Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
- if (moreKeysKeyboard == null) {
- // {@link KeyPreviewDrawParams#mPreviewVisibleWidth} should have been set at
- // {@link KeyPreviewChoreographer#placeKeyPreview(Key,TextView,KeyboardIconsSet,KeyDrawParams,int,int[]},
- // though there may be some chances that the value is zero. <code>width == 0</code>
- // will cause zero-division error at
- // {@link MoreKeysKeyboardParams#setParameters(int,int,int,int,int,int,boolean,int)}.
- final boolean isSingleMoreKeyWithPreview = mKeyPreviewDrawParams.isPopupEnabled()
- && !key.noKeyPreview() && moreKeys.length == 1
- && mKeyPreviewDrawParams.getVisibleWidth() > 0;
- final MoreKeysKeyboard.Builder builder = new MoreKeysKeyboard.Builder(
- getContext(), key, getKeyboard(), isSingleMoreKeyWithPreview,
- mKeyPreviewDrawParams.getVisibleWidth(),
- mKeyPreviewDrawParams.getVisibleHeight(), newLabelPaint(key));
- moreKeysKeyboard = builder.build();
- mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
- }
-
- final View container = key.isActionKey() ? mMoreKeysKeyboardForActionContainer
- : mMoreKeysKeyboardContainer;
- final MoreKeysKeyboardView moreKeysKeyboardView =
- (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
- moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
- container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- final int[] lastCoords = CoordinateUtils.newInstance();
- tracker.getLastCoordinates(lastCoords);
- final boolean keyPreviewEnabled = mKeyPreviewDrawParams.isPopupEnabled()
- && !key.noKeyPreview();
- // The more keys keyboard is usually horizontally aligned with the center of the parent key.
- // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
- // keys keyboard is placed at the touch point of the parent key.
- final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
- ? CoordinateUtils.x(lastCoords)
- : key.getX() + key.getWidth() / 2;
- // The more keys keyboard is usually vertically aligned with the top edge of the parent key
- // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
- // aligned with the bottom edge of the visible part of the key preview.
- // {@code mPreviewVisibleOffset} has been set appropriately in
- // {@link KeyboardView#showKeyPreview(PointerTracker)}.
- final int pointY = key.getY() + mKeyPreviewDrawParams.getVisibleOffset();
- moreKeysKeyboardView.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
- return moreKeysKeyboardView;
- }
-
- public boolean isInDraggingFinger() {
- if (isShowingMoreKeysPanel()) {
- return true;
- }
- return PointerTracker.isAnyInDraggingFinger();
- }
-
- @Override
- public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
- locatePreviewPlacerView();
- // Dismiss another {@link MoreKeysPanel} that may be being showed.
- onDismissMoreKeysPanel();
- // Dismiss all key previews that may be being showed.
- PointerTracker.setReleasedKeyGraphicsToAllKeys();
- // Dismiss sliding key input preview that may be being showed.
- mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
- panel.showInParent(mDrawingPreviewPlacerView);
- mMoreKeysPanel = panel;
- }
-
- public boolean isShowingMoreKeysPanel() {
- return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
- }
-
- @Override
- public void onCancelMoreKeysPanel() {
- PointerTracker.dismissAllMoreKeysPanels();
- }
-
- @Override
- public void onDismissMoreKeysPanel() {
- if (isShowingMoreKeysPanel()) {
- mMoreKeysPanel.removeFromParent();
- mMoreKeysPanel = null;
- }
- }
-
- public void startDoubleTapShiftKeyTimer() {
- mTimerHandler.startDoubleTapShiftKeyTimer();
- }
-
- public void cancelDoubleTapShiftKeyTimer() {
- mTimerHandler.cancelDoubleTapShiftKeyTimer();
- }
-
- public boolean isInDoubleTapShiftKeyTimeout() {
- return mTimerHandler.isInDoubleTapShiftKeyTimeout();
- }
-
- @Override
- public boolean onTouchEvent(final MotionEvent event) {
- if (getKeyboard() == null) {
- return false;
- }
- if (mNonDistinctMultitouchHelper != null) {
- if (event.getPointerCount() > 1 && mTimerHandler.isInKeyRepeat()) {
- // Key repeating timer will be canceled if 2 or more keys are in action.
- mTimerHandler.cancelKeyRepeatTimers();
- }
- // Non distinct multitouch screen support
- mNonDistinctMultitouchHelper.processMotionEvent(event, mKeyDetector);
- return true;
- }
- return processMotionEvent(event);
- }
-
- public boolean processMotionEvent(final MotionEvent event) {
- final int index = event.getActionIndex();
- final int id = event.getPointerId(index);
- final PointerTracker tracker = PointerTracker.getPointerTracker(id);
- // When a more keys panel is showing, we should ignore other fingers' single touch events
- // other than the finger that is showing the more keys panel.
- if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
- && PointerTracker.getActivePointerTrackerCount() == 1) {
- return true;
- }
- tracker.processMotionEvent(event, mKeyDetector);
- return true;
- }
-
- public void cancelAllOngoingEvents() {
- mTimerHandler.cancelAllMessages();
- PointerTracker.setReleasedKeyGraphicsToAllKeys();
- mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText();
- mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
- PointerTracker.dismissAllMoreKeysPanels();
- PointerTracker.cancelAllPointerTrackers();
- }
-
- public void closing() {
- cancelAllOngoingEvents();
- mMoreKeysKeyboardCache.clear();
- }
-
- public void onHideWindow() {
- onDismissMoreKeysPanel();
- final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
- if (accessibilityDelegate != null
- && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- accessibilityDelegate.onHideWindow();
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onHoverEvent(final MotionEvent event) {
- final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
- if (accessibilityDelegate != null
- && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return accessibilityDelegate.onHoverEvent(event);
- }
- return super.onHoverEvent(event);
- }
-
- public void updateShortcutKey(final boolean available) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return;
- }
- final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
- if (shortcutKey == null) {
- return;
- }
- shortcutKey.setEnabled(available);
- invalidateKey(shortcutKey);
- }
-
- public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
- final int languageOnSpacebarFormatType,
- final boolean hasMultipleEnabledIMEsOrSubtypes) {
- if (subtypeChanged) {
- KeyPreviewView.clearTextCache();
- }
- mLanguageOnSpacebarFormatType = languageOnSpacebarFormatType;
- mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
- final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
- if (animator == null) {
- mLanguageOnSpacebarFormatType = LanguageOnSpacebarUtils.FORMAT_TYPE_NONE;
- } else {
- if (subtypeChanged
- && languageOnSpacebarFormatType != LanguageOnSpacebarUtils.FORMAT_TYPE_NONE) {
- setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
- if (animator.isStarted()) {
- animator.cancel();
- }
- animator.start();
- } else {
- if (!animator.isStarted()) {
- mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
- }
- }
- }
- invalidateKey(mSpaceKey);
- }
-
- @Override
- protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
- final KeyDrawParams params) {
- if (key.altCodeWhileTyping() && key.isEnabled()) {
- params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
- }
- super.onDrawKeyTopVisuals(key, canvas, paint, params);
- final int code = key.getCode();
- if (code == Constants.CODE_SPACE) {
- // If input language are explicitly selected.
- if (mLanguageOnSpacebarFormatType != LanguageOnSpacebarUtils.FORMAT_TYPE_NONE) {
- drawLanguageOnSpacebar(key, canvas, paint);
- }
- // Whether space key needs to show the "..." popup hint for special purposes
- if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
- drawKeyPopupHint(key, canvas, paint, params);
- }
- } else if (code == Constants.CODE_LANGUAGE_SWITCH) {
- drawKeyPopupHint(key, canvas, paint, params);
- }
- }
-
- private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
- final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
- paint.setTextScaleX(1.0f);
- final float textWidth = TypefaceUtils.getStringWidth(text, paint);
- if (textWidth < width) {
- return true;
- }
-
- final float scaleX = maxTextWidth / textWidth;
- if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
- return false;
- }
-
- paint.setTextScaleX(scaleX);
- return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth;
- }
-
- // Layout language name on spacebar.
- private String layoutLanguageOnSpacebar(final Paint paint,
- final RichInputMethodSubtype subtype, final int width) {
- // Choose appropriate language name to fit into the width.
- if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarUtils.FORMAT_TYPE_FULL_LOCALE) {
- final String fullText = subtype.getFullDisplayName();
- if (fitsTextIntoWidth(width, fullText, paint)) {
- return fullText;
- }
- }
-
- final String middleText = subtype.getMiddleDisplayName();
- if (fitsTextIntoWidth(width, middleText, paint)) {
- return middleText;
- }
-
- return "";
- }
-
- private void drawLanguageOnSpacebar(final Key key, final Canvas canvas, final Paint paint) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return;
- }
- final int width = key.getWidth();
- final int height = key.getHeight();
- paint.setTextAlign(Align.CENTER);
- paint.setTypeface(Typeface.DEFAULT);
- paint.setTextSize(mLanguageOnSpacebarTextSize);
- final String language = layoutLanguageOnSpacebar(paint, keyboard.mId.mSubtype, width);
- // Draw language text with shadow
- final float descent = paint.descent();
- final float textHeight = -paint.ascent() + descent;
- final float baseline = height / 2 + textHeight / 2;
- if (mLanguageOnSpacebarTextShadowRadius > 0.0f) {
- paint.setShadowLayer(mLanguageOnSpacebarTextShadowRadius, 0, 0,
- mLanguageOnSpacebarTextShadowColor);
- } else {
- paint.clearShadowLayer();
- }
- paint.setColor(mLanguageOnSpacebarTextColor);
- paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
- canvas.drawText(language, width / 2, baseline - descent, paint);
- paint.clearShadowLayer();
- paint.setTextScaleX(1.0f);
- }
-
- @Override
- public void deallocateMemory() {
- super.deallocateMemory();
- mDrawingPreviewPlacerView.deallocateMemory();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
deleted file mode 100644
index 5a9d4755f..000000000
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-public final class MoreKeysDetector extends KeyDetector {
- private final int mSlideAllowanceSquare;
- private final int mSlideAllowanceSquareTop;
-
- public MoreKeysDetector(float slideAllowance) {
- super();
- mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance);
- // Top slide allowance is slightly longer (sqrt(2) times) than other edges.
- mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2;
- }
-
- @Override
- public boolean alwaysAllowsKeySelectionByDraggingFinger() {
- return true;
- }
-
- @Override
- public Key detectHitKey(final int x, final int y) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard == null) {
- return null;
- }
- final int touchX = getTouchX(x);
- final int touchY = getTouchY(y);
-
- Key nearestKey = null;
- int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
- for (final Key key : keyboard.getSortedKeys()) {
- final int dist = key.squaredDistanceToEdge(touchX, touchY);
- if (dist < nearestDist) {
- nearestKey = key;
- nearestDist = dist;
- }
- }
- return nearestKey;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
deleted file mode 100644
index a021e5e2d..000000000
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.Context;
-import android.graphics.Paint;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
-import com.android.inputmethod.keyboard.internal.MoreKeySpec;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.TypefaceUtils;
-
-import javax.annotation.Nonnull;
-
-public final class MoreKeysKeyboard extends Keyboard {
- private final int mDefaultKeyCoordX;
-
- MoreKeysKeyboard(final MoreKeysKeyboardParams params) {
- super(params);
- mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
- }
-
- public int getDefaultCoordX() {
- return mDefaultKeyCoordX;
- }
-
- @UsedForTesting
- static class MoreKeysKeyboardParams extends KeyboardParams {
- public boolean mIsMoreKeysFixedOrder;
- /* package */int mTopRowAdjustment;
- public int mNumRows;
- public int mNumColumns;
- public int mTopKeys;
- public int mLeftKeys;
- public int mRightKeys; // includes default key.
- public int mDividerWidth;
- public int mColumnWidth;
-
- public MoreKeysKeyboardParams() {
- super();
- }
-
- /**
- * Set keyboard parameters of more keys keyboard.
- *
- * @param numKeys number of keys in this more keys keyboard.
- * @param numColumn number of columns of this more keys keyboard.
- * @param keyWidth more keys keyboard key width in pixel, including horizontal gap.
- * @param rowHeight more keys keyboard row height in pixel, including vertical gap.
- * @param coordXInParent coordinate x of the key preview in parent keyboard.
- * @param parentKeyboardWidth parent keyboard width in pixel.
- * @param isMoreKeysFixedColumn true if more keys keyboard should have
- * <code>numColumn</code> columns. Otherwise more keys keyboard should have
- * <code>numColumn</code> columns at most.
- * @param isMoreKeysFixedOrder true if the order of more keys is determined by the order in
- * the more keys' specification. Otherwise the order of more keys is automatically
- * determined.
- * @param dividerWidth width of divider, zero for no dividers.
- */
- public void setParameters(final int numKeys, final int numColumn, final int keyWidth,
- final int rowHeight, final int coordXInParent, final int parentKeyboardWidth,
- final boolean isMoreKeysFixedColumn, final boolean isMoreKeysFixedOrder,
- final int dividerWidth) {
- mIsMoreKeysFixedOrder = isMoreKeysFixedOrder;
- if (parentKeyboardWidth / keyWidth < Math.min(numKeys, numColumn)) {
- throw new IllegalArgumentException("Keyboard is too small to hold more keys: "
- + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + numColumn);
- }
- mDefaultKeyWidth = keyWidth;
- mDefaultRowHeight = rowHeight;
-
- final int numRows = (numKeys + numColumn - 1) / numColumn;
- mNumRows = numRows;
- final int numColumns = isMoreKeysFixedColumn ? Math.min(numKeys, numColumn)
- : getOptimizedColumns(numKeys, numColumn);
- mNumColumns = numColumns;
- final int topKeys = numKeys % numColumns;
- mTopKeys = topKeys == 0 ? numColumns : topKeys;
-
- final int numLeftKeys = (numColumns - 1) / 2;
- final int numRightKeys = numColumns - numLeftKeys; // including default key.
- // Maximum number of keys we can layout both side of the parent key
- final int maxLeftKeys = coordXInParent / keyWidth;
- final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth;
- int leftKeys, rightKeys;
- if (numLeftKeys > maxLeftKeys) {
- leftKeys = maxLeftKeys;
- rightKeys = numColumns - leftKeys;
- } else if (numRightKeys > maxRightKeys + 1) {
- rightKeys = maxRightKeys + 1; // include default key
- leftKeys = numColumns - rightKeys;
- } else {
- leftKeys = numLeftKeys;
- rightKeys = numRightKeys;
- }
- // If the left keys fill the left side of the parent key, entire more keys keyboard
- // should be shifted to the right unless the parent key is on the left edge.
- if (maxLeftKeys == leftKeys && leftKeys > 0) {
- leftKeys--;
- rightKeys++;
- }
- // If the right keys fill the right side of the parent key, entire more keys
- // should be shifted to the left unless the parent key is on the right edge.
- if (maxRightKeys == rightKeys - 1 && rightKeys > 1) {
- leftKeys++;
- rightKeys--;
- }
- mLeftKeys = leftKeys;
- mRightKeys = rightKeys;
-
- // Adjustment of the top row.
- mTopRowAdjustment = isMoreKeysFixedOrder ? getFixedOrderTopRowAdjustment()
- : getAutoOrderTopRowAdjustment();
- mDividerWidth = dividerWidth;
- mColumnWidth = mDefaultKeyWidth + mDividerWidth;
- mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth - mDividerWidth;
- // Need to subtract the bottom row's gutter only.
- mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
- + mTopPadding + mBottomPadding;
- }
-
- private int getFixedOrderTopRowAdjustment() {
- if (mNumRows == 1 || mTopKeys % 2 == 1 || mTopKeys == mNumColumns
- || mLeftKeys == 0 || mRightKeys == 1) {
- return 0;
- }
- return -1;
- }
-
- private int getAutoOrderTopRowAdjustment() {
- if (mNumRows == 1 || mTopKeys == 1 || mNumColumns % 2 == mTopKeys % 2
- || mLeftKeys == 0 || mRightKeys == 1) {
- return 0;
- }
- return -1;
- }
-
- // Return key position according to column count (0 is default).
- /* package */int getColumnPos(final int n) {
- return mIsMoreKeysFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
- }
-
- private int getFixedOrderColumnPos(final int n) {
- final int col = n % mNumColumns;
- final int row = n / mNumColumns;
- if (!isTopRow(row)) {
- return col - mLeftKeys;
- }
- final int rightSideKeys = mTopKeys / 2;
- final int leftSideKeys = mTopKeys - (rightSideKeys + 1);
- final int pos = col - leftSideKeys;
- final int numLeftKeys = mLeftKeys + mTopRowAdjustment;
- final int numRightKeys = mRightKeys - 1;
- if (numRightKeys >= rightSideKeys && numLeftKeys >= leftSideKeys) {
- return pos;
- } else if (numRightKeys < rightSideKeys) {
- return pos - (rightSideKeys - numRightKeys);
- } else { // numLeftKeys < leftSideKeys
- return pos + (leftSideKeys - numLeftKeys);
- }
- }
-
- private int getAutomaticColumnPos(final int n) {
- final int col = n % mNumColumns;
- final int row = n / mNumColumns;
- int leftKeys = mLeftKeys;
- if (isTopRow(row)) {
- leftKeys += mTopRowAdjustment;
- }
- if (col == 0) {
- // default position.
- return 0;
- }
-
- int pos = 0;
- int right = 1; // include default position key.
- int left = 0;
- int i = 0;
- while (true) {
- // Assign right key if available.
- if (right < mRightKeys) {
- pos = right;
- right++;
- i++;
- }
- if (i >= col)
- break;
- // Assign left key if available.
- if (left < leftKeys) {
- left++;
- pos = -left;
- i++;
- }
- if (i >= col)
- break;
- }
- return pos;
- }
-
- private static int getTopRowEmptySlots(final int numKeys, final int numColumns) {
- final int remainings = numKeys % numColumns;
- return remainings == 0 ? 0 : numColumns - remainings;
- }
-
- private int getOptimizedColumns(final int numKeys, final int maxColumns) {
- int numColumns = Math.min(numKeys, maxColumns);
- while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
- numColumns--;
- }
- return numColumns;
- }
-
- public int getDefaultKeyCoordX() {
- return mLeftKeys * mColumnWidth + mLeftPadding;
- }
-
- public int getX(final int n, final int row) {
- final int x = getColumnPos(n) * mColumnWidth + getDefaultKeyCoordX();
- if (isTopRow(row)) {
- return x + mTopRowAdjustment * (mColumnWidth / 2);
- }
- return x;
- }
-
- public int getY(final int row) {
- return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
- }
-
- public void markAsEdgeKey(final Key key, final int row) {
- if (row == 0)
- key.markAsTopEdge(this);
- if (isTopRow(row))
- key.markAsBottomEdge(this);
- }
-
- private boolean isTopRow(final int rowCount) {
- return mNumRows > 1 && rowCount == mNumRows - 1;
- }
- }
-
- public static class Builder extends KeyboardBuilder<MoreKeysKeyboardParams> {
- private final Key mParentKey;
-
- private static final float LABEL_PADDING_RATIO = 0.2f;
- private static final float DIVIDER_RATIO = 0.2f;
-
- /**
- * The builder of MoreKeysKeyboard.
- * @param context the context of {@link MoreKeysKeyboardView}.
- * @param key the {@link Key} that invokes more keys keyboard.
- * @param keyboard the {@link Keyboard} that contains the parentKey.
- * @param isSingleMoreKeyWithPreview true if the <code>key</code> has just a single
- * "more key" and its key popup preview is enabled.
- * @param keyPreviewVisibleWidth the width of visible part of key popup preview.
- * @param keyPreviewVisibleHeight the height of visible part of key popup preview
- * @param paintToMeasure the {@link Paint} object to measure a "more key" width
- */
- public Builder(final Context context, final Key key, final Keyboard keyboard,
- final boolean isSingleMoreKeyWithPreview, final int keyPreviewVisibleWidth,
- final int keyPreviewVisibleHeight, final Paint paintToMeasure) {
- super(context, new MoreKeysKeyboardParams());
- load(keyboard.mMoreKeysTemplate, keyboard.mId);
-
- // TODO: More keys keyboard's vertical gap is currently calculated heuristically.
- // Should revise the algorithm.
- mParams.mVerticalGap = keyboard.mVerticalGap / 2;
- // This {@link MoreKeysKeyboard} is invoked from the <code>key</code>.
- mParentKey = key;
-
- final int keyWidth, rowHeight;
- if (isSingleMoreKeyWithPreview) {
- // Use pre-computed width and height if this more keys keyboard has only one key to
- // mitigate visual flicker between key preview and more keys keyboard.
- // Caveats for the visual assets: To achieve this effect, both the key preview
- // backgrounds and the more keys keyboard panel background have the exact same
- // left/right/top paddings. The bottom paddings of both backgrounds don't need to
- // be considered because the vertical positions of both backgrounds were already
- // adjusted with their bottom paddings deducted.
- keyWidth = keyPreviewVisibleWidth;
- rowHeight = keyPreviewVisibleHeight + mParams.mVerticalGap;
- } else {
- final float padding = context.getResources().getDimension(
- R.dimen.config_more_keys_keyboard_key_horizontal_padding)
- + (key.hasLabelsInMoreKeys()
- ? mParams.mDefaultKeyWidth * LABEL_PADDING_RATIO : 0.0f);
- keyWidth = getMaxKeyWidth(key, mParams.mDefaultKeyWidth, padding, paintToMeasure);
- rowHeight = keyboard.mMostCommonKeyHeight;
- }
- final int dividerWidth;
- if (key.needsDividersInMoreKeys()) {
- dividerWidth = (int)(keyWidth * DIVIDER_RATIO);
- } else {
- dividerWidth = 0;
- }
- final MoreKeySpec[] moreKeys = key.getMoreKeys();
- mParams.setParameters(moreKeys.length, key.getMoreKeysColumnNumber(), keyWidth,
- rowHeight, key.getX() + key.getWidth() / 2, keyboard.mId.mWidth,
- key.isMoreKeysFixedColumn(), key.isMoreKeysFixedOrder(), dividerWidth);
- }
-
- private static int getMaxKeyWidth(final Key parentKey, final int minKeyWidth,
- final float padding, final Paint paint) {
- int maxWidth = minKeyWidth;
- for (final MoreKeySpec spec : parentKey.getMoreKeys()) {
- final String label = spec.mLabel;
- // If the label is single letter, minKeyWidth is enough to hold the label.
- if (label != null && StringUtils.codePointCount(label) > 1) {
- maxWidth = Math.max(maxWidth,
- (int)(TypefaceUtils.getStringWidth(label, paint) + padding));
- }
- }
- return maxWidth;
- }
-
- @Override
- @Nonnull
- public MoreKeysKeyboard build() {
- final MoreKeysKeyboardParams params = mParams;
- final int moreKeyFlags = mParentKey.getMoreKeyLabelFlags();
- final MoreKeySpec[] moreKeys = mParentKey.getMoreKeys();
- for (int n = 0; n < moreKeys.length; n++) {
- final MoreKeySpec moreKeySpec = moreKeys[n];
- final int row = n / params.mNumColumns;
- final int x = params.getX(n, row);
- final int y = params.getY(row);
- final Key key = moreKeySpec.buildKey(x, y, moreKeyFlags, params);
- params.markAsEdgeKey(key, row);
- params.onAddKey(key);
-
- final int pos = params.getColumnPos(n);
- // The "pos" value represents the offset from the default position. Negative means
- // left of the default position.
- if (params.mDividerWidth > 0 && pos != 0) {
- final int dividerX = (pos > 0) ? x - params.mDividerWidth
- : x + params.mDefaultKeyWidth;
- final Key divider = new MoreKeyDivider(
- params, dividerX, y, params.mDividerWidth, params.mDefaultRowHeight);
- params.onAddKey(divider);
- }
- }
- return new MoreKeysKeyboard(params);
- }
- }
-
- // Used as a divider maker. A divider is drawn by {@link MoreKeysKeyboardView}.
- public static class MoreKeyDivider extends Key.Spacer {
- public MoreKeyDivider(final KeyboardParams params, final int x, final int y,
- final int width, final int height) {
- super(params, x, y, width, height);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
deleted file mode 100644
index 3acc11b59..000000000
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate;
-import com.android.inputmethod.keyboard.internal.KeyDrawParams;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-
-/**
- * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
- * detecting key presses and touch movements.
- */
-public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
- private final int[] mCoordinates = CoordinateUtils.newInstance();
-
- private final Drawable mDivider;
- protected final KeyDetector mKeyDetector;
- private Controller mController = EMPTY_CONTROLLER;
- protected KeyboardActionListener mListener;
- private int mOriginX;
- private int mOriginY;
- private Key mCurrentKey;
-
- private int mActivePointerId;
-
- protected MoreKeysKeyboardAccessibilityDelegate mAccessibilityDelegate;
-
- public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
- }
-
- public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
- final int defStyle) {
- super(context, attrs, defStyle);
- final TypedArray moreKeysKeyboardViewAttr = context.obtainStyledAttributes(attrs,
- R.styleable.MoreKeysKeyboardView, defStyle, R.style.MoreKeysKeyboardView);
- mDivider = moreKeysKeyboardViewAttr.getDrawable(R.styleable.MoreKeysKeyboardView_divider);
- if (mDivider != null) {
- // TODO: Drawable itself should have an alpha value.
- mDivider.setAlpha(128);
- }
- moreKeysKeyboardViewAttr.recycle();
- mKeyDetector = new MoreKeysDetector(getResources().getDimension(
- R.dimen.config_more_keys_keyboard_slide_allowance));
- }
-
- @Override
- protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard != null) {
- final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
- final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
- setMeasuredDimension(width, height);
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
- final KeyDrawParams params) {
- if (!key.isSpacer() || !(key instanceof MoreKeysKeyboard.MoreKeyDivider)
- || mDivider == null) {
- super.onDrawKeyTopVisuals(key, canvas, paint, params);
- return;
- }
- final int keyWidth = key.getDrawWidth();
- final int keyHeight = key.getHeight();
- final int iconWidth = Math.min(mDivider.getIntrinsicWidth(), keyWidth);
- final int iconHeight = mDivider.getIntrinsicHeight();
- final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center
- final int iconY = (keyHeight - iconHeight) / 2; // Align vertically center
- drawIcon(canvas, mDivider, iconX, iconY, iconWidth, iconHeight);
- }
-
- @Override
- public void setKeyboard(final Keyboard keyboard) {
- super.setKeyboard(keyboard);
- mKeyDetector.setKeyboard(
- keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
- if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- if (mAccessibilityDelegate == null) {
- mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(
- this, mKeyDetector);
- mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard);
- mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard);
- }
- mAccessibilityDelegate.setKeyboard(keyboard);
- } else {
- mAccessibilityDelegate = null;
- }
- }
-
- @Override
- public void showMoreKeysPanel(final View parentView, final Controller controller,
- final int pointX, final int pointY, final KeyboardActionListener listener) {
- mController = controller;
- mListener = listener;
- final View container = getContainerView();
- // The coordinates of panel's left-top corner in parentView's coordinate system.
- // We need to consider background drawable paddings.
- final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft();
- final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom()
- + getPaddingBottom();
-
- parentView.getLocationInWindow(mCoordinates);
- // Ensure the horizontal position of the panel does not extend past the parentView edges.
- final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
- final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates);
- final int panelY = y + CoordinateUtils.y(mCoordinates);
- container.setX(panelX);
- container.setY(panelY);
-
- mOriginX = x + container.getPaddingLeft();
- mOriginY = y + container.getPaddingTop();
- controller.onShowMoreKeysPanel(this);
- final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
- if (accessibilityDelegate != null
- && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- accessibilityDelegate.onShowMoreKeysKeyboard();
- }
- }
-
- /**
- * Returns the default x coordinate for showing this panel.
- */
- protected int getDefaultCoordX() {
- return ((MoreKeysKeyboard)getKeyboard()).getDefaultCoordX();
- }
-
- @Override
- public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) {
- mActivePointerId = pointerId;
- mCurrentKey = detectKey(x, y);
- }
-
- @Override
- public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) {
- if (mActivePointerId != pointerId) {
- return;
- }
- final boolean hasOldKey = (mCurrentKey != null);
- mCurrentKey = detectKey(x, y);
- if (hasOldKey && mCurrentKey == null) {
- // A more keys keyboard is canceled when detecting no key.
- mController.onCancelMoreKeysPanel();
- }
- }
-
- @Override
- public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
- if (mActivePointerId != pointerId) {
- return;
- }
- // Calling {@link #detectKey(int,int,int)} here is harmless because the last move event and
- // the following up event share the same coordinates.
- mCurrentKey = detectKey(x, y);
- if (mCurrentKey != null) {
- updateReleaseKeyGraphics(mCurrentKey);
- onKeyInput(mCurrentKey, x, y);
- mCurrentKey = null;
- }
- }
-
- /**
- * Performs the specific action for this panel when the user presses a key on the panel.
- */
- protected void onKeyInput(final Key key, final int x, final int y) {
- final int code = key.getCode();
- if (code == Constants.CODE_OUTPUT_TEXT) {
- mListener.onTextInput(mCurrentKey.getOutputText());
- } else if (code != Constants.CODE_UNSPECIFIED) {
- if (getKeyboard().hasProximityCharsCorrection(code)) {
- mListener.onCodeInput(code, x, y, false /* isKeyRepeat */);
- } else {
- mListener.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
- false /* isKeyRepeat */);
- }
- }
- }
-
- private Key detectKey(int x, int y) {
- final Key oldKey = mCurrentKey;
- final Key newKey = mKeyDetector.detectHitKey(x, y);
- if (newKey == oldKey) {
- return newKey;
- }
- // A new key is detected.
- if (oldKey != null) {
- updateReleaseKeyGraphics(oldKey);
- invalidateKey(oldKey);
- }
- if (newKey != null) {
- updatePressKeyGraphics(newKey);
- invalidateKey(newKey);
- }
- return newKey;
- }
-
- private void updateReleaseKeyGraphics(final Key key) {
- key.onReleased();
- invalidateKey(key);
- }
-
- private void updatePressKeyGraphics(final Key key) {
- key.onPressed();
- invalidateKey(key);
- }
-
- @Override
- public void dismissMoreKeysPanel() {
- if (!isShowingInParent()) {
- return;
- }
- final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
- if (accessibilityDelegate != null
- && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- accessibilityDelegate.onDismissMoreKeysKeyboard();
- }
- mController.onDismissMoreKeysPanel();
- }
-
- @Override
- public int translateX(final int x) {
- return x - mOriginX;
- }
-
- @Override
- public int translateY(final int y) {
- return y - mOriginY;
- }
-
- @Override
- public boolean onTouchEvent(final MotionEvent me) {
- final int action = me.getActionMasked();
- final long eventTime = me.getEventTime();
- final int index = me.getActionIndex();
- final int x = (int)me.getX(index);
- final int y = (int)me.getY(index);
- final int pointerId = me.getPointerId(index);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- onDownEvent(x, y, pointerId, eventTime);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- onUpEvent(x, y, pointerId, eventTime);
- break;
- case MotionEvent.ACTION_MOVE:
- onMoveEvent(x, y, pointerId, eventTime);
- break;
- }
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onHoverEvent(final MotionEvent event) {
- final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
- if (accessibilityDelegate != null
- && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return accessibilityDelegate.onHoverEvent(event);
- }
- return super.onHoverEvent(event);
- }
-
- private View getContainerView() {
- return (View)getParent();
- }
-
- @Override
- public void showInParent(final ViewGroup parentView) {
- removeFromParent();
- parentView.addView(getContainerView());
- }
-
- @Override
- public void removeFromParent() {
- final View containerView = getContainerView();
- final ViewGroup currentParent = (ViewGroup)containerView.getParent();
- if (currentParent != null) {
- currentParent.removeView(containerView);
- }
- }
-
- @Override
- public boolean isShowingInParent() {
- return (getContainerView().getParent() != null);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
deleted file mode 100644
index 7bddd09f6..000000000
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-public interface MoreKeysPanel {
- public interface Controller {
- /**
- * Add the {@link MoreKeysPanel} to the target view.
- * @param panel the panel to be shown.
- */
- public void onShowMoreKeysPanel(final MoreKeysPanel panel);
-
- /**
- * Remove the current {@link MoreKeysPanel} from the target view.
- */
- public void onDismissMoreKeysPanel();
-
- /**
- * Instructs the parent to cancel the panel (e.g., when entering a different input mode).
- */
- public void onCancelMoreKeysPanel();
- }
-
- public static final Controller EMPTY_CONTROLLER = new Controller() {
- @Override
- public void onShowMoreKeysPanel(final MoreKeysPanel panel) {}
- @Override
- public void onDismissMoreKeysPanel() {}
- @Override
- public void onCancelMoreKeysPanel() {}
- };
-
- /**
- * Initializes the layout and event handling of this {@link MoreKeysPanel} and calls the
- * controller's onShowMoreKeysPanel to add the panel's container view.
- *
- * @param parentView the parent view of this {@link MoreKeysPanel}
- * @param controller the controller that can dismiss this {@link MoreKeysPanel}
- * @param pointX x coordinate of this {@link MoreKeysPanel}
- * @param pointY y coordinate of this {@link MoreKeysPanel}
- * @param listener the listener that will receive keyboard action from this
- * {@link MoreKeysPanel}.
- */
- // TODO: Currently the MoreKeysPanel is inside a container view that is added to the parent.
- // Consider the simpler approach of placing the MoreKeysPanel itself into the parent view.
- public void showMoreKeysPanel(View parentView, Controller controller, int pointX,
- int pointY, KeyboardActionListener listener);
-
- /**
- * Dismisses the more keys panel and calls the controller's onDismissMoreKeysPanel to remove
- * the panel's container view.
- */
- public void dismissMoreKeysPanel();
-
- /**
- * Process a move event on the more keys panel.
- *
- * @param x translated x coordinate of the touch point
- * @param y translated y coordinate of the touch point
- * @param pointerId pointer id touch point
- * @param eventTime timestamp of touch point
- */
- public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime);
-
- /**
- * Process a down event on the more keys panel.
- *
- * @param x translated x coordinate of the touch point
- * @param y translated y coordinate of the touch point
- * @param pointerId pointer id touch point
- * @param eventTime timestamp of touch point
- */
- public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime);
-
- /**
- * Process an up event on the more keys panel.
- *
- * @param x translated x coordinate of the touch point
- * @param y translated y coordinate of the touch point
- * @param pointerId pointer id touch point
- * @param eventTime timestamp of touch point
- */
- public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime);
-
- /**
- * Translate X-coordinate of touch event to the local X-coordinate of this
- * {@link MoreKeysPanel}.
- *
- * @param x the global X-coordinate
- * @return the local X-coordinate to this {@link MoreKeysPanel}
- */
- public int translateX(int x);
-
- /**
- * Translate Y-coordinate of touch event to the local Y-coordinate of this
- * {@link MoreKeysPanel}.
- *
- * @param y the global Y-coordinate
- * @return the local Y-coordinate to this {@link MoreKeysPanel}
- */
- public int translateY(int y);
-
- /**
- * Show this {@link MoreKeysPanel} in the parent view.
- *
- * @param parentView the {@link ViewGroup} that hosts this {@link MoreKeysPanel}.
- */
- public void showInParent(ViewGroup parentView);
-
- /**
- * Remove this {@link MoreKeysPanel} from the parent view.
- */
- public void removeFromParent();
-
- /**
- * Return whether the panel is currently being shown.
- */
- public boolean isShowingInParent();
-}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
deleted file mode 100644
index c0ac1c054..000000000
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ /dev/null
@@ -1,1198 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.inputmethod.keyboard.internal.BatchInputArbiter;
-import com.android.inputmethod.keyboard.internal.BatchInputArbiter.BatchInputArbiterListener;
-import com.android.inputmethod.keyboard.internal.BogusMoveEventDetector;
-import com.android.inputmethod.keyboard.internal.DrawingProxy;
-import com.android.inputmethod.keyboard.internal.GestureEnabler;
-import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingParams;
-import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingPoints;
-import com.android.inputmethod.keyboard.internal.GestureStrokeRecognitionParams;
-import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
-import com.android.inputmethod.keyboard.internal.TimerProxy;
-import com.android.inputmethod.keyboard.internal.TypingTimeRecorder;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class PointerTracker implements PointerTrackerQueue.Element,
- BatchInputArbiterListener {
- private static final String TAG = PointerTracker.class.getSimpleName();
- private static final boolean DEBUG_EVENT = false;
- private static final boolean DEBUG_MOVE_EVENT = false;
- private static final boolean DEBUG_LISTENER = false;
- private static boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED || DEBUG_EVENT;
-
- static final class PointerTrackerParams {
- public final boolean mKeySelectionByDraggingFinger;
- public final int mTouchNoiseThresholdTime;
- public final int mTouchNoiseThresholdDistance;
- public final int mSuppressKeyPreviewAfterBatchInputDuration;
- public final int mKeyRepeatStartTimeout;
- public final int mKeyRepeatInterval;
- public final int mLongPressShiftLockTimeout;
-
- public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
- mKeySelectionByDraggingFinger = mainKeyboardViewAttr.getBoolean(
- R.styleable.MainKeyboardView_keySelectionByDraggingFinger, false);
- mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
- mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
- R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
- mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
- mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
- mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyRepeatInterval, 0);
- mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
- }
- }
-
- private static GestureEnabler sGestureEnabler = new GestureEnabler();
-
- // Parameters for pointer handling.
- private static PointerTrackerParams sParams;
- private static GestureStrokeRecognitionParams sGestureStrokeRecognitionParams;
- private static GestureStrokeDrawingParams sGestureStrokeDrawingParams;
- private static boolean sNeedsPhantomSuddenMoveEventHack;
- // Move this threshold to resource.
- // TODO: Device specific parameter would be better for device specific hack?
- private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
-
- private static final ArrayList<PointerTracker> sTrackers = new ArrayList<>();
- private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
-
- public final int mPointerId;
-
- private static DrawingProxy sDrawingProxy;
- private static TimerProxy sTimerProxy;
- private static KeyboardActionListener sListener = KeyboardActionListener.EMPTY_LISTENER;
-
- // The {@link KeyDetector} is set whenever the down event is processed. Also this is updated
- // when new {@link Keyboard} is set by {@link #setKeyDetector(KeyDetector)}.
- private KeyDetector mKeyDetector = new KeyDetector();
- private Keyboard mKeyboard;
- private int mPhantomSuddenMoveThreshold;
- private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
-
- private boolean mIsDetectingGesture = false; // per PointerTracker.
- private static boolean sInGesture = false;
- private static TypingTimeRecorder sTypingTimeRecorder;
-
- // The position and time at which first down event occurred.
- private long mDownTime;
- @Nonnull
- private int[] mDownCoordinates = CoordinateUtils.newInstance();
- private long mUpTime;
-
- // The current key where this pointer is.
- private Key mCurrentKey = null;
- // The position where the current key was recognized for the first time.
- private int mKeyX;
- private int mKeyY;
-
- // Last pointer position.
- private int mLastX;
- private int mLastY;
-
- // true if keyboard layout has been changed.
- private boolean mKeyboardLayoutHasBeenChanged;
-
- // true if this pointer is no longer triggering any action because it has been canceled.
- private boolean mIsTrackingForActionDisabled;
-
- // the more keys panel currently being shown. equals null if no panel is active.
- private MoreKeysPanel mMoreKeysPanel;
-
- private static final int MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT = 3;
- // true if this pointer is in the dragging finger mode.
- boolean mIsInDraggingFinger;
- // true if this pointer is sliding from a modifier key and in the sliding key input mode,
- // so that further modifier keys should be ignored.
- boolean mIsInSlidingKeyInput;
- // if not a NOT_A_CODE, the key of this code is repeating
- private int mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
-
- // true if dragging finger is allowed.
- private boolean mIsAllowedDraggingFinger;
-
- private final BatchInputArbiter mBatchInputArbiter;
- private final GestureStrokeDrawingPoints mGestureStrokeDrawingPoints;
-
- // TODO: Add PointerTrackerFactory singleton and move some class static methods into it.
- public static void init(final TypedArray mainKeyboardViewAttr, final TimerProxy timerProxy,
- final DrawingProxy drawingProxy) {
- sParams = new PointerTrackerParams(mainKeyboardViewAttr);
- sGestureStrokeRecognitionParams = new GestureStrokeRecognitionParams(mainKeyboardViewAttr);
- sGestureStrokeDrawingParams = new GestureStrokeDrawingParams(mainKeyboardViewAttr);
- sTypingTimeRecorder = new TypingTimeRecorder(
- sGestureStrokeRecognitionParams.mStaticTimeThresholdAfterFastTyping,
- sParams.mSuppressKeyPreviewAfterBatchInputDuration);
-
- final Resources res = mainKeyboardViewAttr.getResources();
- sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
- ResourceUtils.getDeviceOverrideValue(res,
- R.array.phantom_sudden_move_event_device_list, Boolean.FALSE.toString()));
- BogusMoveEventDetector.init(res);
-
- sTimerProxy = timerProxy;
- sDrawingProxy = drawingProxy;
- }
-
- // Note that this method is called from a non-UI thread.
- public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
- sGestureEnabler.setMainDictionaryAvailability(mainDictionaryAvailable);
- }
-
- public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
- sGestureEnabler.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
- }
-
- public static PointerTracker getPointerTracker(final int id) {
- final ArrayList<PointerTracker> trackers = sTrackers;
-
- // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
- for (int i = trackers.size(); i <= id; i++) {
- final PointerTracker tracker = new PointerTracker(i);
- trackers.add(tracker);
- }
-
- return trackers.get(id);
- }
-
- public static boolean isAnyInDraggingFinger() {
- return sPointerTrackerQueue.isAnyInDraggingFinger();
- }
-
- public static void cancelAllPointerTrackers() {
- sPointerTrackerQueue.cancelAllPointerTrackers();
- }
-
- public static void setKeyboardActionListener(final KeyboardActionListener listener) {
- sListener = listener;
- }
-
- public static void setKeyDetector(final KeyDetector keyDetector) {
- final Keyboard keyboard = keyDetector.getKeyboard();
- if (keyboard == null) {
- return;
- }
- final int trackersSize = sTrackers.size();
- for (int i = 0; i < trackersSize; ++i) {
- final PointerTracker tracker = sTrackers.get(i);
- tracker.setKeyDetectorInner(keyDetector);
- }
- sGestureEnabler.setPasswordMode(keyboard.mId.passwordInput());
- }
-
- public static void setReleasedKeyGraphicsToAllKeys() {
- final int trackersSize = sTrackers.size();
- for (int i = 0; i < trackersSize; ++i) {
- final PointerTracker tracker = sTrackers.get(i);
- tracker.setReleasedKeyGraphics(tracker.getKey(), true /* withAnimation */);
- }
- }
-
- public static void dismissAllMoreKeysPanels() {
- final int trackersSize = sTrackers.size();
- for (int i = 0; i < trackersSize; ++i) {
- final PointerTracker tracker = sTrackers.get(i);
- tracker.dismissMoreKeysPanel();
- }
- }
-
- private PointerTracker(final int id) {
- mPointerId = id;
- mBatchInputArbiter = new BatchInputArbiter(id, sGestureStrokeRecognitionParams);
- mGestureStrokeDrawingPoints = new GestureStrokeDrawingPoints(sGestureStrokeDrawingParams);
- }
-
- // Returns true if keyboard has been changed by this callback.
- private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key,
- final int repeatCount) {
- // While gesture input is going on, this method should be a no-operation. But when gesture
- // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
- // are set to false. To keep this method is a no-operation,
- // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
- if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
- return false;
- }
- final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onPress : %s%s%s%s", mPointerId,
- (key == null ? "none" : Constants.printableCode(key.getCode())),
- ignoreModifierKey ? " ignoreModifier" : "",
- key.isEnabled() ? "" : " disabled",
- repeatCount > 0 ? " repeatCount=" + repeatCount : ""));
- }
- if (ignoreModifierKey) {
- return false;
- }
- if (key.isEnabled()) {
- sListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1);
- final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
- mKeyboardLayoutHasBeenChanged = false;
- sTimerProxy.startTypingStateTimer(key);
- return keyboardLayoutHasBeenChanged;
- }
- return false;
- }
-
- // Note that we need primaryCode argument because the keyboard may in shifted state and the
- // primaryCode is different from {@link Key#mKeyCode}.
- private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
- final int y, final long eventTime, final boolean isKeyRepeat) {
- final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
- final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState();
- final int code = altersCode ? key.getAltCode() : primaryCode;
- if (DEBUG_LISTENER) {
- final String output = code == Constants.CODE_OUTPUT_TEXT
- ? key.getOutputText() : Constants.printableCode(code);
- Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s%s", mPointerId, x, y,
- output, ignoreModifierKey ? " ignoreModifier" : "",
- altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
- }
- if (ignoreModifierKey) {
- return;
- }
- // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
- if (key.isEnabled() || altersCode) {
- sTypingTimeRecorder.onCodeInput(code, eventTime);
- if (code == Constants.CODE_OUTPUT_TEXT) {
- sListener.onTextInput(key.getOutputText());
- } else if (code != Constants.CODE_UNSPECIFIED) {
- if (mKeyboard.hasProximityCharsCorrection(code)) {
- sListener.onCodeInput(code, x, y, isKeyRepeat);
- } else {
- sListener.onCodeInput(code,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, isKeyRepeat);
- }
- }
- }
- }
-
- // Note that we need primaryCode argument because the keyboard may be in shifted state and the
- // primaryCode is different from {@link Key#mKeyCode}.
- private void callListenerOnRelease(final Key key, final int primaryCode,
- final boolean withSliding) {
- // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
- if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
- return;
- }
- final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId,
- Constants.printableCode(primaryCode),
- withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
- key.isEnabled() ? "": " disabled"));
- }
- if (ignoreModifierKey) {
- return;
- }
- if (key.isEnabled()) {
- sListener.onReleaseKey(primaryCode, withSliding);
- }
- }
-
- private void callListenerOnFinishSlidingInput() {
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
- }
- sListener.onFinishSlidingInput();
- }
-
- private void callListenerOnCancelInput() {
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
- }
- sListener.onCancelInput();
- }
-
- private void setKeyDetectorInner(final KeyDetector keyDetector) {
- final Keyboard keyboard = keyDetector.getKeyboard();
- if (keyboard == null) {
- return;
- }
- if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
- return;
- }
- mKeyDetector = keyDetector;
- mKeyboard = keyboard;
- // Mark that keyboard layout has been changed.
- mKeyboardLayoutHasBeenChanged = true;
- final int keyWidth = mKeyboard.mMostCommonKeyWidth;
- final int keyHeight = mKeyboard.mMostCommonKeyHeight;
- mBatchInputArbiter.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
- // Keep {@link #mCurrentKey} that comes from previous keyboard. The key preview of
- // {@link #mCurrentKey} will be dismissed by {@setReleasedKeyGraphics(Key)} via
- // {@link onMoveEventInternal(int,int,long)} or {@link #onUpEventInternal(int,int,long)}.
- mPhantomSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
- mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
- }
-
- @Override
- public boolean isInDraggingFinger() {
- return mIsInDraggingFinger;
- }
-
- @Nullable
- public Key getKey() {
- return mCurrentKey;
- }
-
- @Override
- public boolean isModifier() {
- return mCurrentKey != null && mCurrentKey.isModifier();
- }
-
- public Key getKeyOn(final int x, final int y) {
- return mKeyDetector.detectHitKey(x, y);
- }
-
- private void setReleasedKeyGraphics(@Nullable final Key key, final boolean withAnimation) {
- if (key == null) {
- return;
- }
-
- sDrawingProxy.onKeyReleased(key, withAnimation);
-
- if (key.isShift()) {
- for (final Key shiftKey : mKeyboard.mShiftKeys) {
- if (shiftKey != key) {
- sDrawingProxy.onKeyReleased(shiftKey, false /* withAnimation */);
- }
- }
- }
-
- if (key.altCodeWhileTyping()) {
- final int altCode = key.getAltCode();
- final Key altKey = mKeyboard.getKey(altCode);
- if (altKey != null) {
- sDrawingProxy.onKeyReleased(altKey, false /* withAnimation */);
- }
- for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
- if (k != key && k.getAltCode() == altCode) {
- sDrawingProxy.onKeyReleased(k, false /* withAnimation */);
- }
- }
- }
- }
-
- private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
- if (!sGestureEnabler.shouldHandleGesture()) return false;
- return sTypingTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
- }
-
- private void setPressedKeyGraphics(@Nullable final Key key, final long eventTime) {
- if (key == null) {
- return;
- }
-
- // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
- final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState();
- final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
- if (!needsToUpdateGraphics) {
- return;
- }
-
- final boolean noKeyPreview = sInGesture || needsToSuppressKeyPreviewPopup(eventTime);
- sDrawingProxy.onKeyPressed(key, !noKeyPreview);
-
- if (key.isShift()) {
- for (final Key shiftKey : mKeyboard.mShiftKeys) {
- if (shiftKey != key) {
- sDrawingProxy.onKeyPressed(shiftKey, false /* withPreview */);
- }
- }
- }
-
- if (altersCode) {
- final int altCode = key.getAltCode();
- final Key altKey = mKeyboard.getKey(altCode);
- if (altKey != null) {
- sDrawingProxy.onKeyPressed(altKey, false /* withPreview */);
- }
- for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
- if (k != key && k.getAltCode() == altCode) {
- sDrawingProxy.onKeyPressed(k, false /* withPreview */);
- }
- }
- }
- }
-
- public GestureStrokeDrawingPoints getGestureStrokeDrawingPoints() {
- return mGestureStrokeDrawingPoints;
- }
-
- public void getLastCoordinates(@Nonnull final int[] outCoords) {
- CoordinateUtils.set(outCoords, mLastX, mLastY);
- }
-
- public long getDownTime() {
- return mDownTime;
- }
-
- public void getDownCoordinates(@Nonnull final int[] outCoords) {
- CoordinateUtils.copy(outCoords, mDownCoordinates);
- }
-
- private Key onDownKey(final int x, final int y, final long eventTime) {
- mDownTime = eventTime;
- CoordinateUtils.set(mDownCoordinates, x, y);
- mBogusMoveEventDetector.onDownKey();
- return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
- }
-
- private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
- return (int)Math.hypot(x1 - x2, y1 - y2);
- }
-
- private Key onMoveKeyInternal(final int x, final int y) {
- mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
- mLastX = x;
- mLastY = y;
- return mKeyDetector.detectHitKey(x, y);
- }
-
- private Key onMoveKey(final int x, final int y) {
- return onMoveKeyInternal(x, y);
- }
-
- private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
- mCurrentKey = newKey;
- mKeyX = x;
- mKeyY = y;
- return newKey;
- }
-
- /* package */ static int getActivePointerTrackerCount() {
- return sPointerTrackerQueue.size();
- }
-
- private boolean isOldestTrackerInQueue() {
- return sPointerTrackerQueue.getOldestElement() == this;
- }
-
- // Implements {@link BatchInputArbiterListener}.
- @Override
- public void onStartBatchInput() {
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
- }
- sListener.onStartBatchInput();
- dismissAllMoreKeysPanels();
- sTimerProxy.cancelLongPressTimersOf(this);
- }
-
- private void showGestureTrail() {
- if (mIsTrackingForActionDisabled) {
- return;
- }
- // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
- sDrawingProxy.showGestureTrail(
- this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
- }
-
- public void updateBatchInputByTimer(final long syntheticMoveEventTime) {
- mBatchInputArbiter.updateBatchInputByTimer(syntheticMoveEventTime, this);
- }
-
- // Implements {@link BatchInputArbiterListener}.
- @Override
- public void onUpdateBatchInput(final InputPointers aggregatedPointers, final long eventTime) {
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
- aggregatedPointers.getPointerSize()));
- }
- sListener.onUpdateBatchInput(aggregatedPointers);
- }
-
- // Implements {@link BatchInputArbiterListener}.
- @Override
- public void onStartUpdateBatchInputTimer() {
- sTimerProxy.startUpdateBatchInputTimer(this);
- }
-
- // Implements {@link BatchInputArbiterListener}.
- @Override
- public void onEndBatchInput(final InputPointers aggregatedPointers, final long eventTime) {
- sTypingTimeRecorder.onEndBatchInput(eventTime);
- sTimerProxy.cancelAllUpdateBatchInputTimers();
- if (mIsTrackingForActionDisabled) {
- return;
- }
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
- mPointerId, aggregatedPointers.getPointerSize()));
- }
- sListener.onEndBatchInput(aggregatedPointers);
- }
-
- private void cancelBatchInput() {
- cancelAllPointerTrackers();
- mIsDetectingGesture = false;
- if (!sInGesture) {
- return;
- }
- sInGesture = false;
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
- }
- sListener.onCancelBatchInput();
- }
-
- public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
- final int action = me.getActionMasked();
- final long eventTime = me.getEventTime();
- if (action == MotionEvent.ACTION_MOVE) {
- // When this pointer is the only active pointer and is showing a more keys panel,
- // we should ignore other pointers' motion event.
- final boolean shouldIgnoreOtherPointers =
- isShowingMoreKeysPanel() && getActivePointerTrackerCount() == 1;
- final int pointerCount = me.getPointerCount();
- for (int index = 0; index < pointerCount; index++) {
- final int id = me.getPointerId(index);
- if (shouldIgnoreOtherPointers && id != mPointerId) {
- continue;
- }
- final int x = (int)me.getX(index);
- final int y = (int)me.getY(index);
- final PointerTracker tracker = getPointerTracker(id);
- tracker.onMoveEvent(x, y, eventTime, me);
- }
- return;
- }
- final int index = me.getActionIndex();
- final int x = (int)me.getX(index);
- final int y = (int)me.getY(index);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- onDownEvent(x, y, eventTime, keyDetector);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- onUpEvent(x, y, eventTime);
- break;
- case MotionEvent.ACTION_CANCEL:
- onCancelEvent(x, y, eventTime);
- break;
- }
- }
-
- private void onDownEvent(final int x, final int y, final long eventTime,
- final KeyDetector keyDetector) {
- setKeyDetectorInner(keyDetector);
- if (DEBUG_EVENT) {
- printTouchEvent("onDownEvent:", x, y, eventTime);
- }
- // Naive up-to-down noise filter.
- final long deltaT = eventTime - mUpTime;
- if (deltaT < sParams.mTouchNoiseThresholdTime) {
- final int distance = getDistance(x, y, mLastX, mLastY);
- if (distance < sParams.mTouchNoiseThresholdDistance) {
- if (DEBUG_MODE)
- Log.w(TAG, String.format("[%d] onDownEvent:"
- + " ignore potential noise: time=%d distance=%d",
- mPointerId, deltaT, distance));
- cancelTrackingForAction();
- return;
- }
- }
-
- final Key key = getKeyOn(x, y);
- mBogusMoveEventDetector.onActualDownEvent(x, y);
- if (key != null && key.isModifier()) {
- // Before processing a down event of modifier key, all pointers already being
- // tracked should be released.
- sPointerTrackerQueue.releaseAllPointers(eventTime);
- }
- sPointerTrackerQueue.add(this);
- onDownEventInternal(x, y, eventTime);
- if (!sGestureEnabler.shouldHandleGesture()) {
- return;
- }
- // A gesture should start only from a non-modifier key. Note that the gesture detection is
- // disabled when the key is repeating.
- mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
- && key != null && !key.isModifier();
- if (mIsDetectingGesture) {
- mBatchInputArbiter.addDownEventPoint(x, y, eventTime,
- sTypingTimeRecorder.getLastLetterTypingTime(), getActivePointerTrackerCount());
- mGestureStrokeDrawingPoints.onDownEvent(
- x, y, mBatchInputArbiter.getElapsedTimeSinceFirstDown(eventTime));
- }
- }
-
- /* package */ boolean isShowingMoreKeysPanel() {
- return (mMoreKeysPanel != null);
- }
-
- private void dismissMoreKeysPanel() {
- if (isShowingMoreKeysPanel()) {
- mMoreKeysPanel.dismissMoreKeysPanel();
- mMoreKeysPanel = null;
- }
- }
-
- private void onDownEventInternal(final int x, final int y, final long eventTime) {
- Key key = onDownKey(x, y, eventTime);
- // Key selection by dragging finger is allowed when 1) key selection by dragging finger is
- // enabled by configuration, 2) this pointer starts dragging from modifier key, or 3) this
- // pointer's KeyDetector always allows key selection by dragging finger, such as
- // {@link MoreKeysKeyboard}.
- mIsAllowedDraggingFinger = sParams.mKeySelectionByDraggingFinger
- || (key != null && key.isModifier())
- || mKeyDetector.alwaysAllowsKeySelectionByDraggingFinger();
- mKeyboardLayoutHasBeenChanged = false;
- mIsTrackingForActionDisabled = false;
- resetKeySelectionByDraggingFinger();
- if (key != null) {
- // This onPress call may have changed keyboard layout. Those cases are detected at
- // {@link #setKeyboard}. In those cases, we should update key according to the new
- // keyboard layout.
- if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
- key = onDownKey(x, y, eventTime);
- }
-
- startRepeatKey(key);
- startLongPressTimer(key);
- setPressedKeyGraphics(key, eventTime);
- }
- }
-
- private void startKeySelectionByDraggingFinger(final Key key) {
- if (!mIsInDraggingFinger) {
- mIsInSlidingKeyInput = key.isModifier();
- }
- mIsInDraggingFinger = true;
- }
-
- private void resetKeySelectionByDraggingFinger() {
- mIsInDraggingFinger = false;
- mIsInSlidingKeyInput = false;
- sDrawingProxy.showSlidingKeyInputPreview(null /* tracker */);
- }
-
- private void onGestureMoveEvent(final int x, final int y, final long eventTime,
- final boolean isMajorEvent, final Key key) {
- if (!mIsDetectingGesture) {
- return;
- }
- final boolean onValidArea = mBatchInputArbiter.addMoveEventPoint(
- x, y, eventTime, isMajorEvent, this);
- // If the move event goes out from valid batch input area, cancel batch input.
- if (!onValidArea) {
- cancelBatchInput();
- return;
- }
- mGestureStrokeDrawingPoints.onMoveEvent(
- x, y, mBatchInputArbiter.getElapsedTimeSinceFirstDown(eventTime));
- // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
- // the gestured touch points are still being recorded in case the panel is dismissed.
- if (isShowingMoreKeysPanel()) {
- return;
- }
- if (!sInGesture && key != null && Character.isLetter(key.getCode())
- && mBatchInputArbiter.mayStartBatchInput(this)) {
- sInGesture = true;
- }
- if (sInGesture) {
- if (key != null) {
- mBatchInputArbiter.updateBatchInput(eventTime, this);
- }
- showGestureTrail();
- }
- }
-
- private void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
- if (DEBUG_MOVE_EVENT) {
- printTouchEvent("onMoveEvent:", x, y, eventTime);
- }
- if (mIsTrackingForActionDisabled) {
- return;
- }
-
- if (sGestureEnabler.shouldHandleGesture() && me != null) {
- // Add historical points to gesture path.
- final int pointerIndex = me.findPointerIndex(mPointerId);
- final int historicalSize = me.getHistorySize();
- for (int h = 0; h < historicalSize; h++) {
- final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
- final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
- final long historicalTime = me.getHistoricalEventTime(h);
- onGestureMoveEvent(historicalX, historicalY, historicalTime,
- false /* isMajorEvent */, null);
- }
- }
-
- if (isShowingMoreKeysPanel()) {
- final int translatedX = mMoreKeysPanel.translateX(x);
- final int translatedY = mMoreKeysPanel.translateY(y);
- mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
- onMoveKey(x, y);
- if (mIsInSlidingKeyInput) {
- sDrawingProxy.showSlidingKeyInputPreview(this);
- }
- return;
- }
- onMoveEventInternal(x, y, eventTime);
- }
-
- private void processDraggingFingerInToNewKey(final Key newKey, final int x, final int y,
- final long eventTime) {
- // This onPress call may have changed keyboard layout. Those cases are detected
- // at {@link #setKeyboard}. In those cases, we should update key according
- // to the new keyboard layout.
- Key key = newKey;
- if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
- key = onMoveKey(x, y);
- }
- onMoveToNewKey(key, x, y);
- if (mIsTrackingForActionDisabled) {
- return;
- }
- startLongPressTimer(key);
- setPressedKeyGraphics(key, eventTime);
- }
-
- private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
- final long eventTime, final Key oldKey, final int lastX, final int lastY) {
- if (DEBUG_MODE) {
- Log.w(TAG, String.format("[%d] onMoveEvent:"
- + " phantom sudden move event (distance=%d) is translated to "
- + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
- getDistance(x, y, lastX, lastY),
- lastX, lastY, Constants.printableCode(oldKey.getCode()),
- x, y, Constants.printableCode(key.getCode())));
- }
- onUpEventInternal(x, y, eventTime);
- onDownEventInternal(x, y, eventTime);
- }
-
- private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
- final long eventTime, final Key oldKey, final int lastX, final int lastY) {
- if (DEBUG_MODE) {
- final float keyDiagonal = (float)Math.hypot(
- mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
- final float radiusRatio =
- mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
- / keyDiagonal;
- Log.w(TAG, String.format("[%d] onMoveEvent:"
- + " bogus down-move-up event (raidus=%.2f key diagonal) is "
- + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
- mPointerId, radiusRatio,
- lastX, lastY, Constants.printableCode(oldKey.getCode()),
- x, y, Constants.printableCode(key.getCode())));
- }
- onUpEventInternal(x, y, eventTime);
- onDownEventInternal(x, y, eventTime);
- }
-
- private void processDraggingFingerOutFromOldKey(final Key oldKey) {
- setReleasedKeyGraphics(oldKey, true /* withAnimation */);
- callListenerOnRelease(oldKey, oldKey.getCode(), true /* withSliding */);
- startKeySelectionByDraggingFinger(oldKey);
- sTimerProxy.cancelKeyTimersOf(this);
- }
-
- private void dragFingerFromOldKeyToNewKey(final Key key, final int x, final int y,
- final long eventTime, final Key oldKey, final int lastX, final int lastY) {
- // The pointer has been slid in to the new key from the previous key, we must call
- // onRelease() first to notify that the previous key has been released, then call
- // onPress() to notify that the new key is being pressed.
- processDraggingFingerOutFromOldKey(oldKey);
- startRepeatKey(key);
- if (mIsAllowedDraggingFinger) {
- processDraggingFingerInToNewKey(key, x, y, eventTime);
- }
- // HACK: On some devices, quick successive touches may be reported as a sudden move by
- // touch panel firmware. This hack detects such cases and translates the move event to
- // successive up and down events.
- // TODO: Should find a way to balance gesture detection and this hack.
- else if (sNeedsPhantomSuddenMoveEventHack
- && getDistance(x, y, lastX, lastY) >= mPhantomSuddenMoveThreshold) {
- processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
- }
- // HACK: On some devices, quick successive proximate touches may be reported as a bogus
- // down-move-up event by touch panel firmware. This hack detects such cases and breaks
- // these events into separate up and down events.
- else if (sTypingTimeRecorder.isInFastTyping(eventTime)
- && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
- processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
- }
- // HACK: If there are currently multiple touches, register the key even if the finger
- // slides off the key. This defends against noise from some touch panels when there are
- // close multiple touches.
- // Caveat: When in chording input mode with a modifier key, we don't use this hack.
- else if (getActivePointerTrackerCount() > 1
- && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
- if (DEBUG_MODE) {
- Log.w(TAG, String.format("[%d] onMoveEvent:"
- + " detected sliding finger while multi touching", mPointerId));
- }
- onUpEvent(x, y, eventTime);
- cancelTrackingForAction();
- setReleasedKeyGraphics(oldKey, true /* withAnimation */);
- } else {
- if (!mIsDetectingGesture) {
- cancelTrackingForAction();
- }
- setReleasedKeyGraphics(oldKey, true /* withAnimation */);
- }
- }
-
- private void dragFingerOutFromOldKey(final Key oldKey, final int x, final int y) {
- // The pointer has been slid out from the previous key, we must call onRelease() to
- // notify that the previous key has been released.
- processDraggingFingerOutFromOldKey(oldKey);
- if (mIsAllowedDraggingFinger) {
- onMoveToNewKey(null, x, y);
- } else {
- if (!mIsDetectingGesture) {
- cancelTrackingForAction();
- }
- }
- }
-
- private void onMoveEventInternal(final int x, final int y, final long eventTime) {
- final int lastX = mLastX;
- final int lastY = mLastY;
- final Key oldKey = mCurrentKey;
- final Key newKey = onMoveKey(x, y);
-
- if (sGestureEnabler.shouldHandleGesture()) {
- // Register move event on gesture tracker.
- onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
- if (sInGesture) {
- mCurrentKey = null;
- setReleasedKeyGraphics(oldKey, true /* withAnimation */);
- return;
- }
- }
-
- if (newKey != null) {
- if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
- dragFingerFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
- } else if (oldKey == null) {
- // The pointer has been slid in to the new key, but the finger was not on any keys.
- // In this case, we must call onPress() to notify that the new key is being pressed.
- processDraggingFingerInToNewKey(newKey, x, y, eventTime);
- }
- } else { // newKey == null
- if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
- dragFingerOutFromOldKey(oldKey, x, y);
- }
- }
- if (mIsInSlidingKeyInput) {
- sDrawingProxy.showSlidingKeyInputPreview(this);
- }
- }
-
- private void onUpEvent(final int x, final int y, final long eventTime) {
- if (DEBUG_EVENT) {
- printTouchEvent("onUpEvent :", x, y, eventTime);
- }
-
- sTimerProxy.cancelUpdateBatchInputTimer(this);
- if (!sInGesture) {
- if (mCurrentKey != null && mCurrentKey.isModifier()) {
- // Before processing an up event of modifier key, all pointers already being
- // tracked should be released.
- sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
- } else {
- sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
- }
- }
- onUpEventInternal(x, y, eventTime);
- sPointerTrackerQueue.remove(this);
- }
-
- // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
- // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
- // "virtual" up event.
- @Override
- public void onPhantomUpEvent(final long eventTime) {
- if (DEBUG_EVENT) {
- printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
- }
- onUpEventInternal(mLastX, mLastY, eventTime);
- cancelTrackingForAction();
- }
-
- private void onUpEventInternal(final int x, final int y, final long eventTime) {
- sTimerProxy.cancelKeyTimersOf(this);
- final boolean isInDraggingFinger = mIsInDraggingFinger;
- final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
- resetKeySelectionByDraggingFinger();
- mIsDetectingGesture = false;
- final Key currentKey = mCurrentKey;
- mCurrentKey = null;
- final int currentRepeatingKeyCode = mCurrentRepeatingKeyCode;
- mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
- // Release the last pressed key.
- setReleasedKeyGraphics(currentKey, true /* withAnimation */);
-
- if (isShowingMoreKeysPanel()) {
- if (!mIsTrackingForActionDisabled) {
- final int translatedX = mMoreKeysPanel.translateX(x);
- final int translatedY = mMoreKeysPanel.translateY(y);
- mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
- }
- dismissMoreKeysPanel();
- return;
- }
-
- if (sInGesture) {
- if (currentKey != null) {
- callListenerOnRelease(currentKey, currentKey.getCode(), true /* withSliding */);
- }
- if (mBatchInputArbiter.mayEndBatchInput(
- eventTime, getActivePointerTrackerCount(), this)) {
- sInGesture = false;
- }
- showGestureTrail();
- return;
- }
-
- if (mIsTrackingForActionDisabled) {
- return;
- }
- if (currentKey != null && currentKey.isRepeatable()
- && (currentKey.getCode() == currentRepeatingKeyCode) && !isInDraggingFinger) {
- return;
- }
- detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
- if (isInSlidingKeyInput) {
- callListenerOnFinishSlidingInput();
- }
- }
-
- @Override
- public void cancelTrackingForAction() {
- if (isShowingMoreKeysPanel()) {
- return;
- }
- mIsTrackingForActionDisabled = true;
- }
-
- public boolean isInOperation() {
- return !mIsTrackingForActionDisabled;
- }
-
- public void onLongPressed() {
- sTimerProxy.cancelLongPressTimersOf(this);
- if (isShowingMoreKeysPanel()) {
- return;
- }
- final Key key = getKey();
- if (key == null) {
- return;
- }
- if (key.hasNoPanelAutoMoreKey()) {
- cancelKeyTracking();
- final int moreKeyCode = key.getMoreKeys()[0].mCode;
- sListener.onPressKey(moreKeyCode, 0 /* repeatCont */, true /* isSinglePointer */);
- sListener.onCodeInput(moreKeyCode, Constants.NOT_A_COORDINATE,
- Constants.NOT_A_COORDINATE, false /* isKeyRepeat */);
- sListener.onReleaseKey(moreKeyCode, false /* withSliding */);
- return;
- }
- final int code = key.getCode();
- if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
- // Long pressing the space key invokes IME switcher dialog.
- if (sListener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
- cancelKeyTracking();
- sListener.onReleaseKey(code, false /* withSliding */);
- return;
- }
- }
-
- setReleasedKeyGraphics(key, false /* withAnimation */);
- final MoreKeysPanel moreKeysPanel = sDrawingProxy.showMoreKeysKeyboard(key, this);
- if (moreKeysPanel == null) {
- return;
- }
- final int translatedX = moreKeysPanel.translateX(mLastX);
- final int translatedY = moreKeysPanel.translateY(mLastY);
- moreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, SystemClock.uptimeMillis());
- mMoreKeysPanel = moreKeysPanel;
- }
-
- private void cancelKeyTracking() {
- resetKeySelectionByDraggingFinger();
- cancelTrackingForAction();
- setReleasedKeyGraphics(mCurrentKey, true /* withAnimation */);
- sPointerTrackerQueue.remove(this);
- }
-
- private void onCancelEvent(final int x, final int y, final long eventTime) {
- if (DEBUG_EVENT) {
- printTouchEvent("onCancelEvt:", x, y, eventTime);
- }
-
- cancelBatchInput();
- cancelAllPointerTrackers();
- sPointerTrackerQueue.releaseAllPointers(eventTime);
- onCancelEventInternal();
- }
-
- private void onCancelEventInternal() {
- sTimerProxy.cancelKeyTimersOf(this);
- setReleasedKeyGraphics(mCurrentKey, true /* withAnimation */);
- resetKeySelectionByDraggingFinger();
- dismissMoreKeysPanel();
- }
-
- private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
- final Key newKey) {
- final Key curKey = mCurrentKey;
- if (newKey == curKey) {
- return false;
- }
- if (curKey == null /* && newKey != null */) {
- return true;
- }
- // Here curKey points to the different key from newKey.
- final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
- mIsInSlidingKeyInput);
- final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
- if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
- if (DEBUG_MODE) {
- final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
- / mKeyboard.mMostCommonKeyWidth;
- Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
- +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
- }
- return true;
- }
- if (!mIsAllowedDraggingFinger && sTypingTimeRecorder.isInFastTyping(eventTime)
- && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
- if (DEBUG_MODE) {
- final float keyDiagonal = (float)Math.hypot(
- mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
- final float lengthFromDownRatio =
- mBogusMoveEventDetector.getAccumulatedDistanceFromDownKey() / keyDiagonal;
- Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
- + " %.2f key diagonal from virtual down point",
- mPointerId, lengthFromDownRatio));
- }
- return true;
- }
- return false;
- }
-
- private void startLongPressTimer(final Key key) {
- // Note that we need to cancel all active long press shift key timers if any whenever we
- // start a new long press timer for both non-shift and shift keys.
- sTimerProxy.cancelLongPressShiftKeyTimer();
- if (sInGesture) return;
- if (key == null) return;
- if (!key.isLongPressEnabled()) return;
- // Caveat: Please note that isLongPressEnabled() can be true even if the current key
- // doesn't have its more keys. (e.g. spacebar, globe key) If we are in the dragging finger
- // mode, we will disable long press timer of such key.
- // We always need to start the long press timer if the key has its more keys regardless of
- // whether or not we are in the dragging finger mode.
- if (mIsInDraggingFinger && key.getMoreKeys() == null) return;
-
- final int delay = getLongPressTimeout(key.getCode());
- if (delay <= 0) return;
- sTimerProxy.startLongPressTimerOf(this, delay);
- }
-
- private int getLongPressTimeout(final int code) {
- if (code == Constants.CODE_SHIFT) {
- return sParams.mLongPressShiftLockTimeout;
- }
- final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
- if (mIsInSlidingKeyInput) {
- // We use longer timeout for sliding finger input started from the modifier key.
- return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
- }
- return longpressTimeout;
- }
-
- private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
- if (key == null) {
- callListenerOnCancelInput();
- return;
- }
-
- final int code = key.getCode();
- callListenerOnCodeInput(key, code, x, y, eventTime, false /* isKeyRepeat */);
- callListenerOnRelease(key, code, false /* withSliding */);
- }
-
- private void startRepeatKey(final Key key) {
- if (sInGesture) return;
- if (key == null) return;
- if (!key.isRepeatable()) return;
- // Don't start key repeat when we are in the dragging finger mode.
- if (mIsInDraggingFinger) return;
- final int startRepeatCount = 1;
- startKeyRepeatTimer(startRepeatCount);
- }
-
- public void onKeyRepeat(final int code, final int repeatCount) {
- final Key key = getKey();
- if (key == null || key.getCode() != code) {
- mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
- return;
- }
- mCurrentRepeatingKeyCode = code;
- mIsDetectingGesture = false;
- final int nextRepeatCount = repeatCount + 1;
- startKeyRepeatTimer(nextRepeatCount);
- callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
- callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis(),
- true /* isKeyRepeat */);
- }
-
- private void startKeyRepeatTimer(final int repeatCount) {
- final int delay =
- (repeatCount == 1) ? sParams.mKeyRepeatStartTimeout : sParams.mKeyRepeatInterval;
- sTimerProxy.startKeyRepeatTimerOf(this, repeatCount, delay);
- }
-
- private void printTouchEvent(final String title, final int x, final int y,
- final long eventTime) {
- final Key key = mKeyDetector.detectHitKey(x, y);
- final String code = (key == null ? "none" : Constants.printableCode(key.getCode()));
- Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
- (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
deleted file mode 100644
index b9a5eaefb..000000000
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.graphics.Rect;
-import android.util.Log;
-
-import com.android.inputmethod.keyboard.internal.TouchPositionCorrection;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.JniUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-public class ProximityInfo {
- private static final String TAG = ProximityInfo.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- // Must be equal to MAX_PROXIMITY_CHARS_SIZE in native/jni/src/defines.h
- public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
- /** Number of key widths from current touch point to search for nearest keys. */
- private static final float SEARCH_DISTANCE = 1.2f;
- @Nonnull
- private static final List<Key> EMPTY_KEY_LIST = Collections.emptyList();
- private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f;
-
- private final int mGridWidth;
- private final int mGridHeight;
- private final int mGridSize;
- private final int mCellWidth;
- private final int mCellHeight;
- // TODO: Find a proper name for mKeyboardMinWidth
- private final int mKeyboardMinWidth;
- private final int mKeyboardHeight;
- private final int mMostCommonKeyWidth;
- private final int mMostCommonKeyHeight;
- @Nonnull
- private final List<Key> mSortedKeys;
- @Nonnull
- private final List<Key>[] mGridNeighbors;
-
- @SuppressWarnings("unchecked")
- ProximityInfo(final int gridWidth, final int gridHeight, final int minWidth, final int height,
- final int mostCommonKeyWidth, final int mostCommonKeyHeight,
- @Nonnull final List<Key> sortedKeys,
- @Nonnull final TouchPositionCorrection touchPositionCorrection) {
- mGridWidth = gridWidth;
- mGridHeight = gridHeight;
- mGridSize = mGridWidth * mGridHeight;
- mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
- mCellHeight = (height + mGridHeight - 1) / mGridHeight;
- mKeyboardMinWidth = minWidth;
- mKeyboardHeight = height;
- mMostCommonKeyHeight = mostCommonKeyHeight;
- mMostCommonKeyWidth = mostCommonKeyWidth;
- mSortedKeys = sortedKeys;
- mGridNeighbors = new List[mGridSize];
- if (minWidth == 0 || height == 0) {
- // No proximity required. Keyboard might be more keys keyboard.
- return;
- }
- computeNearestNeighbors();
- mNativeProximityInfo = createNativeProximityInfo(touchPositionCorrection);
- }
-
- private long mNativeProximityInfo;
- static {
- JniUtils.loadNativeLibrary();
- }
-
- // TODO: Stop passing proximityCharsArray
- private static native long setProximityInfoNative(int displayWidth, int displayHeight,
- int gridWidth, int gridHeight, int mostCommonKeyWidth, int mostCommonKeyHeight,
- int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
- int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
- float[] sweetSpotCenterYs, float[] sweetSpotRadii);
-
- private static native void releaseProximityInfoNative(long nativeProximityInfo);
-
- static boolean needsProximityInfo(final Key key) {
- // Don't include special keys into ProximityInfo.
- return key.getCode() >= Constants.CODE_SPACE;
- }
-
- private static int getProximityInfoKeysCount(final List<Key> keys) {
- int count = 0;
- for (final Key key : keys) {
- if (needsProximityInfo(key)) {
- count++;
- }
- }
- return count;
- }
-
- private long createNativeProximityInfo(
- @Nonnull final TouchPositionCorrection touchPositionCorrection) {
- final List<Key>[] gridNeighborKeys = mGridNeighbors;
- final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
- Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE);
- for (int i = 0; i < mGridSize; ++i) {
- final List<Key> neighborKeys = gridNeighborKeys[i];
- final int proximityCharsLength = neighborKeys.size();
- int infoIndex = i * MAX_PROXIMITY_CHARS_SIZE;
- for (int j = 0; j < proximityCharsLength; ++j) {
- final Key neighborKey = neighborKeys.get(j);
- // Excluding from proximityCharsArray
- if (!needsProximityInfo(neighborKey)) {
- continue;
- }
- proximityCharsArray[infoIndex] = neighborKey.getCode();
- infoIndex++;
- }
- }
- if (DEBUG) {
- final StringBuilder sb = new StringBuilder();
- for (int i = 0; i < mGridSize; i++) {
- sb.setLength(0);
- for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; j++) {
- final int code = proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j];
- if (code == Constants.NOT_A_CODE) {
- break;
- }
- if (sb.length() > 0) sb.append(" ");
- sb.append(Constants.printableCode(code));
- }
- Log.d(TAG, "proxmityChars["+i+"]: " + sb);
- }
- }
-
- final List<Key> sortedKeys = mSortedKeys;
- final int keyCount = getProximityInfoKeysCount(sortedKeys);
- final int[] keyXCoordinates = new int[keyCount];
- final int[] keyYCoordinates = new int[keyCount];
- final int[] keyWidths = new int[keyCount];
- final int[] keyHeights = new int[keyCount];
- final int[] keyCharCodes = new int[keyCount];
- final float[] sweetSpotCenterXs;
- final float[] sweetSpotCenterYs;
- final float[] sweetSpotRadii;
-
- for (int infoIndex = 0, keyIndex = 0; keyIndex < sortedKeys.size(); keyIndex++) {
- final Key key = sortedKeys.get(keyIndex);
- // Excluding from key coordinate arrays
- if (!needsProximityInfo(key)) {
- continue;
- }
- keyXCoordinates[infoIndex] = key.getX();
- keyYCoordinates[infoIndex] = key.getY();
- keyWidths[infoIndex] = key.getWidth();
- keyHeights[infoIndex] = key.getHeight();
- keyCharCodes[infoIndex] = key.getCode();
- infoIndex++;
- }
-
- if (touchPositionCorrection.isValid()) {
- if (DEBUG) {
- Log.d(TAG, "touchPositionCorrection: ON");
- }
- sweetSpotCenterXs = new float[keyCount];
- sweetSpotCenterYs = new float[keyCount];
- sweetSpotRadii = new float[keyCount];
- final int rows = touchPositionCorrection.getRows();
- final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
- * (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight);
- for (int infoIndex = 0, keyIndex = 0; keyIndex < sortedKeys.size(); keyIndex++) {
- final Key key = sortedKeys.get(keyIndex);
- // Excluding from touch position correction arrays
- if (!needsProximityInfo(key)) {
- continue;
- }
- final Rect hitBox = key.getHitBox();
- sweetSpotCenterXs[infoIndex] = hitBox.exactCenterX();
- sweetSpotCenterYs[infoIndex] = hitBox.exactCenterY();
- sweetSpotRadii[infoIndex] = defaultRadius;
- final int row = hitBox.top / mMostCommonKeyHeight;
- if (row < rows) {
- final int hitBoxWidth = hitBox.width();
- final int hitBoxHeight = hitBox.height();
- final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight);
- sweetSpotCenterXs[infoIndex] +=
- touchPositionCorrection.getX(row) * hitBoxWidth;
- sweetSpotCenterYs[infoIndex] +=
- touchPositionCorrection.getY(row) * hitBoxHeight;
- sweetSpotRadii[infoIndex] =
- touchPositionCorrection.getRadius(row) * hitBoxDiagonal;
- }
- if (DEBUG) {
- Log.d(TAG, String.format(
- " [%2d] row=%d x/y/r=%7.2f/%7.2f/%5.2f %s code=%s", infoIndex, row,
- sweetSpotCenterXs[infoIndex], sweetSpotCenterYs[infoIndex],
- sweetSpotRadii[infoIndex], (row < rows ? "correct" : "default"),
- Constants.printableCode(key.getCode())));
- }
- infoIndex++;
- }
- } else {
- sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
- if (DEBUG) {
- Log.d(TAG, "touchPositionCorrection: OFF");
- }
- }
-
- // TODO: Stop passing proximityCharsArray
- return setProximityInfoNative(mKeyboardMinWidth, mKeyboardHeight, mGridWidth, mGridHeight,
- mMostCommonKeyWidth, mMostCommonKeyHeight, proximityCharsArray, keyCount,
- keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
- sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
- }
-
- public long getNativeProximityInfo() {
- return mNativeProximityInfo;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mNativeProximityInfo != 0) {
- releaseProximityInfoNative(mNativeProximityInfo);
- mNativeProximityInfo = 0;
- }
- } finally {
- super.finalize();
- }
- }
-
- private void computeNearestNeighbors() {
- final int defaultWidth = mMostCommonKeyWidth;
- final int keyCount = mSortedKeys.size();
- final int gridSize = mGridNeighbors.length;
- final int threshold = (int) (defaultWidth * SEARCH_DISTANCE);
- final int thresholdSquared = threshold * threshold;
- // Round-up so we don't have any pixels outside the grid
- final int lastPixelXCoordinate = mGridWidth * mCellWidth - 1;
- final int lastPixelYCoordinate = mGridHeight * mCellHeight - 1;
-
- // For large layouts, 'neighborsFlatBuffer' is about 80k of memory: gridSize is usually 512,
- // keycount is about 40 and a pointer to a Key is 4 bytes. This contains, for each cell,
- // enough space for as many keys as there are on the keyboard. Hence, every
- // keycount'th element is the start of a new cell, and each of these virtual subarrays
- // start empty with keycount spaces available. This fills up gradually in the loop below.
- // Since in the practice each cell does not have a lot of neighbors, most of this space is
- // actually just empty padding in this fixed-size buffer.
- final Key[] neighborsFlatBuffer = new Key[gridSize * keyCount];
- final int[] neighborCountPerCell = new int[gridSize];
- final int halfCellWidth = mCellWidth / 2;
- final int halfCellHeight = mCellHeight / 2;
- for (final Key key : mSortedKeys) {
- if (key.isSpacer()) continue;
-
-/* HOW WE PRE-SELECT THE CELLS (iterate over only the relevant cells, instead of all of them)
-
- We want to compute the distance for keys that are in the cells that are close enough to the
- key border, as this method is performance-critical. These keys are represented with 'star'
- background on the diagram below. Let's consider the Y case first.
-
- We want to select the cells which center falls between the top of the key minus the threshold,
- and the bottom of the key plus the threshold.
- topPixelWithinThreshold is key.mY - threshold, and bottomPixelWithinThreshold is
- key.mY + key.mHeight + threshold.
-
- Then we need to compute the center of the top row that we need to evaluate, as we'll iterate
- from there.
-
-(0,0)----> x
-| .-------------------------------------------.
-| | | | | | | | | | | | |
-| |---+---+---+---+---+---+---+---+---+---+---| .- top of top cell (aligned on the grid)
-| | | | | | | | | | | | | |
-| |-----------+---+---+---+---+---+---+---+---|---' v
-| | | | |***|***|*_________________________ topPixelWithinThreshold | yDeltaToGrid
-| |---+---+---+-----^-+-|-+---+---+---+---+---| ^
-| | | | |***|*|*|*|*|***|***| | | | ______________________________________
-v |---+---+--threshold--|-+---+---+---+---+---| |
- | | | |***|*|*|*|*|***|***| | | | | Starting from key.mY, we substract
-y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get the top pixel
- | | | |***|**########------------------- key.mY | within the threshold. We align that on
- |---+---+---+---+--#+---+-#-+---+---+---+---| | the grid by computing the delta to the
- | | | |***|**#|***|*#*|***| | | | | grid, and get the top of the top cell.
- |---+---+---+---+--#+---+-#-+---+---+---+---| |
- | | | |***|**########*|***| | | | | Adding half the cell height to the top
- |---+---+---+---+---+-|-+---+---+---+---+---| | of the top cell, we get the middle of
- | | | |***|***|*|*|***|***| | | | | the top cell (yMiddleOfTopCell).
- |---+---+---+---+---+-|-+---+---+---+---+---| |
- | | | |***|***|*|*|***|***| | | | |
- |---+---+---+---+---+-|________________________ yEnd | Since we only want to add the key to
- | | | | | | | (bottomPixelWithinThreshold) | the proximity if it's close enough to
- |---+---+---+---+---+---+---+---+---+---+---| | the center of the cell, we only need
- | | | | | | | | | | | | | to compute for these cells where
- '---'---'---'---'---'---'---'---'---'---'---' | topPixelWithinThreshold is above the
- (positive x,y) | center of the cell. This is the case
- | when yDeltaToGrid is less than half
- [Zoomed in diagram] | the height of the cell.
- +-------+-------+-------+-------+-------+ |
- | | | | | | | On the zoomed in diagram, on the right
- | | | | | | | the topPixelWithinThreshold (represented
- | | | | | | top of | with an = sign) is below and we can skip
- +-------+-------+-------+--v----+-------+ .. top cell | this cell, while on the left it's above
- | | = topPixelWT | | yDeltaToGrid | and we need to compute for this cell.
- |..yStart.|.....|.......|..|....|.......|... y middle | Thus, if yDeltaToGrid is more than half
- | (left)| | | ^ = | | of top cell | the height of the cell, we start the
- +-------+-|-----+-------+----|--+-------+ | iteration one cell below the top cell,
- | | | | | | | | | else we start it on the top cell. This
- |.......|.|.....|.......|....|..|.....yStart (right) | is stored in yStart.
-
- Since we only want to go up to bottomPixelWithinThreshold, and we only iterate on the center
- of the keys, we can stop as soon as the y value exceeds bottomPixelThreshold, so we don't
- have to align this on the center of the key. Hence, we don't need a separate value for
- bottomPixelWithinThreshold and call this yEnd right away.
-*/
- final int keyX = key.getX();
- final int keyY = key.getY();
- final int topPixelWithinThreshold = keyY - threshold;
- final int yDeltaToGrid = topPixelWithinThreshold % mCellHeight;
- final int yMiddleOfTopCell = topPixelWithinThreshold - yDeltaToGrid + halfCellHeight;
- final int yStart = Math.max(halfCellHeight,
- yMiddleOfTopCell + (yDeltaToGrid <= halfCellHeight ? 0 : mCellHeight));
- final int yEnd = Math.min(lastPixelYCoordinate, keyY + key.getHeight() + threshold);
-
- final int leftPixelWithinThreshold = keyX - threshold;
- final int xDeltaToGrid = leftPixelWithinThreshold % mCellWidth;
- final int xMiddleOfLeftCell = leftPixelWithinThreshold - xDeltaToGrid + halfCellWidth;
- final int xStart = Math.max(halfCellWidth,
- xMiddleOfLeftCell + (xDeltaToGrid <= halfCellWidth ? 0 : mCellWidth));
- final int xEnd = Math.min(lastPixelXCoordinate, keyX + key.getWidth() + threshold);
-
- int baseIndexOfCurrentRow = (yStart / mCellHeight) * mGridWidth + (xStart / mCellWidth);
- for (int centerY = yStart; centerY <= yEnd; centerY += mCellHeight) {
- int index = baseIndexOfCurrentRow;
- for (int centerX = xStart; centerX <= xEnd; centerX += mCellWidth) {
- if (key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) {
- neighborsFlatBuffer[index * keyCount + neighborCountPerCell[index]] = key;
- ++neighborCountPerCell[index];
- }
- ++index;
- }
- baseIndexOfCurrentRow += mGridWidth;
- }
- }
-
- for (int i = 0; i < gridSize; ++i) {
- final int indexStart = i * keyCount;
- final int indexEnd = indexStart + neighborCountPerCell[i];
- final ArrayList<Key> neighbors = new ArrayList<>(indexEnd - indexStart);
- for (int index = indexStart; index < indexEnd; index++) {
- neighbors.add(neighborsFlatBuffer[index]);
- }
- mGridNeighbors[i] = Collections.unmodifiableList(neighbors);
- }
- }
-
- public void fillArrayWithNearestKeyCodes(final int x, final int y, final int primaryKeyCode,
- final int[] dest) {
- final int destLength = dest.length;
- if (destLength < 1) {
- return;
- }
- int index = 0;
- if (primaryKeyCode > Constants.CODE_SPACE) {
- dest[index++] = primaryKeyCode;
- }
- final List<Key> nearestKeys = getNearestKeys(x, y);
- for (Key key : nearestKeys) {
- if (index >= destLength) {
- break;
- }
- final int code = key.getCode();
- if (code <= Constants.CODE_SPACE) {
- break;
- }
- dest[index++] = code;
- }
- if (index < destLength) {
- dest[index] = Constants.NOT_A_CODE;
- }
- }
-
- @Nonnull
- public List<Key> getNearestKeys(final int x, final int y) {
- if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
- int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
- if (index < mGridSize) {
- return mGridNeighbors[index];
- }
- }
- return EMPTY_KEY_LIST;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/emoji/DynamicGridKeyboard.java
deleted file mode 100644
index daeb1f928..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/DynamicGridKeyboard.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import android.content.SharedPreferences;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.JsonUtils;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This is a Keyboard class where you can add keys dynamically shown in a grid layout
- */
-final class DynamicGridKeyboard extends Keyboard {
- private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
- private static final int TEMPLATE_KEY_CODE_0 = 0x30;
- private static final int TEMPLATE_KEY_CODE_1 = 0x31;
- private final Object mLock = new Object();
-
- private final SharedPreferences mPrefs;
- private final int mHorizontalStep;
- private final int mVerticalStep;
- private final int mColumnsNum;
- private final int mMaxKeyCount;
- private final boolean mIsRecents;
- private final ArrayDeque<GridKey> mGridKeys = new ArrayDeque<>();
- private final ArrayDeque<Key> mPendingKeys = new ArrayDeque<>();
-
- private List<Key> mCachedGridKeys;
-
- public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
- final int maxKeyCount, final int categoryId) {
- super(templateKeyboard);
- final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
- final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
- mHorizontalStep = Math.abs(key1.getX() - key0.getX());
- mVerticalStep = key0.getHeight() + mVerticalGap;
- mColumnsNum = mBaseWidth / mHorizontalStep;
- mMaxKeyCount = maxKeyCount;
- mIsRecents = categoryId == EmojiCategory.ID_RECENTS;
- mPrefs = prefs;
- }
-
- private Key getTemplateKey(final int code) {
- for (final Key key : super.getSortedKeys()) {
- if (key.getCode() == code) {
- return key;
- }
- }
- throw new RuntimeException("Can't find template key: code=" + code);
- }
-
- public void addPendingKey(final Key usedKey) {
- synchronized (mLock) {
- mPendingKeys.addLast(usedKey);
- }
- }
-
- public void flushPendingRecentKeys() {
- synchronized (mLock) {
- while (!mPendingKeys.isEmpty()) {
- addKey(mPendingKeys.pollFirst(), true);
- }
- saveRecentKeys();
- }
- }
-
- public void addKeyFirst(final Key usedKey) {
- addKey(usedKey, true);
- if (mIsRecents) {
- saveRecentKeys();
- }
- }
-
- public void addKeyLast(final Key usedKey) {
- addKey(usedKey, false);
- }
-
- private void addKey(final Key usedKey, final boolean addFirst) {
- if (usedKey == null) {
- return;
- }
- synchronized (mLock) {
- mCachedGridKeys = null;
- final GridKey key = new GridKey(usedKey);
- while (mGridKeys.remove(key)) {
- // Remove duplicate keys.
- }
- if (addFirst) {
- mGridKeys.addFirst(key);
- } else {
- mGridKeys.addLast(key);
- }
- while (mGridKeys.size() > mMaxKeyCount) {
- mGridKeys.removeLast();
- }
- int index = 0;
- for (final GridKey gridKey : mGridKeys) {
- final int keyX0 = getKeyX0(index);
- final int keyY0 = getKeyY0(index);
- final int keyX1 = getKeyX1(index);
- final int keyY1 = getKeyY1(index);
- gridKey.updateCoordinates(keyX0, keyY0, keyX1, keyY1);
- index++;
- }
- }
- }
-
- private void saveRecentKeys() {
- final ArrayList<Object> keys = new ArrayList<>();
- for (final Key key : mGridKeys) {
- if (key.getOutputText() != null) {
- keys.add(key.getOutputText());
- } else {
- keys.add(key.getCode());
- }
- }
- final String jsonStr = JsonUtils.listToJsonStr(keys);
- Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
- }
-
- private static Key getKeyByCode(final Collection<DynamicGridKeyboard> keyboards,
- final int code) {
- for (final DynamicGridKeyboard keyboard : keyboards) {
- for (final Key key : keyboard.getSortedKeys()) {
- if (key.getCode() == code) {
- return key;
- }
- }
- }
- return null;
- }
-
- private static Key getKeyByOutputText(final Collection<DynamicGridKeyboard> keyboards,
- final String outputText) {
- for (final DynamicGridKeyboard keyboard : keyboards) {
- for (final Key key : keyboard.getSortedKeys()) {
- if (outputText.equals(key.getOutputText())) {
- return key;
- }
- }
- }
- return null;
- }
-
- public void loadRecentKeys(final Collection<DynamicGridKeyboard> keyboards) {
- final String str = Settings.readEmojiRecentKeys(mPrefs);
- final List<Object> keys = JsonUtils.jsonStrToList(str);
- for (final Object o : keys) {
- final Key key;
- if (o instanceof Integer) {
- final int code = (Integer)o;
- key = getKeyByCode(keyboards, code);
- } else if (o instanceof String) {
- final String outputText = (String)o;
- key = getKeyByOutputText(keyboards, outputText);
- } else {
- Log.w(TAG, "Invalid object: " + o);
- continue;
- }
- addKeyLast(key);
- }
- }
-
- private int getKeyX0(final int index) {
- final int column = index % mColumnsNum;
- return column * mHorizontalStep;
- }
-
- private int getKeyX1(final int index) {
- final int column = index % mColumnsNum + 1;
- return column * mHorizontalStep;
- }
-
- private int getKeyY0(final int index) {
- final int row = index / mColumnsNum;
- return row * mVerticalStep + mVerticalGap / 2;
- }
-
- private int getKeyY1(final int index) {
- final int row = index / mColumnsNum + 1;
- return row * mVerticalStep + mVerticalGap / 2;
- }
-
- @Override
- public List<Key> getSortedKeys() {
- synchronized (mLock) {
- if (mCachedGridKeys != null) {
- return mCachedGridKeys;
- }
- final ArrayList<Key> cachedKeys = new ArrayList<Key>(mGridKeys);
- mCachedGridKeys = Collections.unmodifiableList(cachedKeys);
- return mCachedGridKeys;
- }
- }
-
- @Override
- public List<Key> getNearestKeys(final int x, final int y) {
- // TODO: Calculate the nearest key index in mGridKeys from x and y.
- return getSortedKeys();
- }
-
- static final class GridKey extends Key {
- private int mCurrentX;
- private int mCurrentY;
-
- public GridKey(final Key originalKey) {
- super(originalKey);
- }
-
- public void updateCoordinates(final int x0, final int y0, final int x1, final int y1) {
- mCurrentX = x0;
- mCurrentY = y0;
- getHitBox().set(x0, y0, x1, y1);
- }
-
- @Override
- public int getX() {
- return mCurrentX;
- }
-
- @Override
- public int getY() {
- return mCurrentY;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (!(o instanceof Key)) return false;
- final Key key = (Key)o;
- if (getCode() != key.getCode()) return false;
- if (!TextUtils.equals(getLabel(), key.getLabel())) return false;
- return TextUtils.equals(getOutputText(), key.getOutputText());
- }
-
- @Override
- public String toString() {
- return "GridKey: " + super.toString();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java
deleted file mode 100644
index b57e483d1..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.inputmethod.compat.BuildCompatUtils;
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.settings.Settings;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-final class EmojiCategory {
- private final String TAG = EmojiCategory.class.getSimpleName();
-
- private static final int ID_UNSPECIFIED = -1;
- public static final int ID_RECENTS = 0;
- private static final int ID_PEOPLE = 1;
- private static final int ID_OBJECTS = 2;
- private static final int ID_NATURE = 3;
- private static final int ID_PLACES = 4;
- private static final int ID_SYMBOLS = 5;
- private static final int ID_EMOTICONS = 6;
- private static final int ID_FLAGS = 7;
- private static final int ID_EIGHT_SMILEY_PEOPLE = 8;
- private static final int ID_EIGHT_ANIMALS_NATURE = 9;
- private static final int ID_EIGHT_FOOD_DRINK = 10;
- private static final int ID_EIGHT_TRAVEL_PLACES = 11;
- private static final int ID_EIGHT_ACTIVITY = 12;
- private static final int ID_EIGHT_OBJECTS = 13;
- private static final int ID_EIGHT_SYMBOLS = 14;
- private static final int ID_EIGHT_FLAGS = 15;
- private static final int ID_EIGHT_SMILEY_PEOPLE_BORING = 16;
-
- public final class CategoryProperties {
- public final int mCategoryId;
- public final int mPageCount;
- public CategoryProperties(final int categoryId, final int pageCount) {
- mCategoryId = categoryId;
- mPageCount = pageCount;
- }
- }
-
- private static final String[] sCategoryName = {
- "recents",
- "people",
- "objects",
- "nature",
- "places",
- "symbols",
- "emoticons",
- "flags",
- "smiley & people",
- "animals & nature",
- "food & drink",
- "travel & places",
- "activity",
- "objects2",
- "symbols2",
- "flags2",
- "smiley & people2" };
-
- private static final int[] sCategoryTabIconAttr = {
- R.styleable.EmojiPalettesView_iconEmojiRecentsTab,
- R.styleable.EmojiPalettesView_iconEmojiCategory1Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory2Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory3Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory4Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory5Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory6Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory7Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory8Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory9Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory10Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory11Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory12Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory13Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory14Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory15Tab,
- R.styleable.EmojiPalettesView_iconEmojiCategory16Tab };
-
- private static final int[] sAccessibilityDescriptionResourceIdsForCategories = {
- R.string.spoken_descrption_emoji_category_recents,
- R.string.spoken_descrption_emoji_category_people,
- R.string.spoken_descrption_emoji_category_objects,
- R.string.spoken_descrption_emoji_category_nature,
- R.string.spoken_descrption_emoji_category_places,
- R.string.spoken_descrption_emoji_category_symbols,
- R.string.spoken_descrption_emoji_category_emoticons,
- R.string.spoken_descrption_emoji_category_flags,
- R.string.spoken_descrption_emoji_category_eight_smiley_people,
- R.string.spoken_descrption_emoji_category_eight_animals_nature,
- R.string.spoken_descrption_emoji_category_eight_food_drink,
- R.string.spoken_descrption_emoji_category_eight_travel_places,
- R.string.spoken_descrption_emoji_category_eight_activity,
- R.string.spoken_descrption_emoji_category_objects,
- R.string.spoken_descrption_emoji_category_symbols,
- R.string.spoken_descrption_emoji_category_flags,
- R.string.spoken_descrption_emoji_category_eight_smiley_people };
-
- private static final int[] sCategoryElementId = {
- KeyboardId.ELEMENT_EMOJI_RECENTS,
- KeyboardId.ELEMENT_EMOJI_CATEGORY1,
- KeyboardId.ELEMENT_EMOJI_CATEGORY2,
- KeyboardId.ELEMENT_EMOJI_CATEGORY3,
- KeyboardId.ELEMENT_EMOJI_CATEGORY4,
- KeyboardId.ELEMENT_EMOJI_CATEGORY5,
- KeyboardId.ELEMENT_EMOJI_CATEGORY6,
- KeyboardId.ELEMENT_EMOJI_CATEGORY7,
- KeyboardId.ELEMENT_EMOJI_CATEGORY8,
- KeyboardId.ELEMENT_EMOJI_CATEGORY9,
- KeyboardId.ELEMENT_EMOJI_CATEGORY10,
- KeyboardId.ELEMENT_EMOJI_CATEGORY11,
- KeyboardId.ELEMENT_EMOJI_CATEGORY12,
- KeyboardId.ELEMENT_EMOJI_CATEGORY13,
- KeyboardId.ELEMENT_EMOJI_CATEGORY14,
- KeyboardId.ELEMENT_EMOJI_CATEGORY15,
- KeyboardId.ELEMENT_EMOJI_CATEGORY16 };
-
- private final SharedPreferences mPrefs;
- private final Resources mRes;
- private final int mMaxPageKeyCount;
- private final KeyboardLayoutSet mLayoutSet;
- private final HashMap<String, Integer> mCategoryNameToIdMap = new HashMap<>();
- private final int[] mCategoryTabIconId = new int[sCategoryName.length];
- private final ArrayList<CategoryProperties> mShownCategories = new ArrayList<>();
- private final ConcurrentHashMap<Long, DynamicGridKeyboard> mCategoryKeyboardMap =
- new ConcurrentHashMap<>();
-
- private int mCurrentCategoryId = EmojiCategory.ID_UNSPECIFIED;
- private int mCurrentCategoryPageId = 0;
-
- public EmojiCategory(final SharedPreferences prefs, final Resources res,
- final KeyboardLayoutSet layoutSet, final TypedArray emojiPaletteViewAttr) {
- mPrefs = prefs;
- mRes = res;
- mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count);
- mLayoutSet = layoutSet;
- for (int i = 0; i < sCategoryName.length; ++i) {
- mCategoryNameToIdMap.put(sCategoryName[i], i);
- mCategoryTabIconId[i] = emojiPaletteViewAttr.getResourceId(
- sCategoryTabIconAttr[i], 0);
- }
-
- int defaultCategoryId = EmojiCategory.ID_SYMBOLS;
- addShownCategoryId(EmojiCategory.ID_RECENTS);
- if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.KITKAT) {
- if (canShowUnicodeEightEmoji()) {
- defaultCategoryId = EmojiCategory.ID_EIGHT_SMILEY_PEOPLE;
- addShownCategoryId(EmojiCategory.ID_EIGHT_SMILEY_PEOPLE);
- addShownCategoryId(EmojiCategory.ID_EIGHT_ANIMALS_NATURE);
- addShownCategoryId(EmojiCategory.ID_EIGHT_FOOD_DRINK);
- addShownCategoryId(EmojiCategory.ID_EIGHT_TRAVEL_PLACES);
- addShownCategoryId(EmojiCategory.ID_EIGHT_ACTIVITY);
- addShownCategoryId(EmojiCategory.ID_EIGHT_OBJECTS);
- addShownCategoryId(EmojiCategory.ID_EIGHT_SYMBOLS);
- addShownCategoryId(EmojiCategory.ID_FLAGS); // Exclude combinations without glyphs.
- } else {
- defaultCategoryId = EmojiCategory.ID_PEOPLE;
- addShownCategoryId(EmojiCategory.ID_PEOPLE);
- addShownCategoryId(EmojiCategory.ID_OBJECTS);
- addShownCategoryId(EmojiCategory.ID_NATURE);
- addShownCategoryId(EmojiCategory.ID_PLACES);
- addShownCategoryId(EmojiCategory.ID_SYMBOLS);
- if (canShowFlagEmoji()) {
- addShownCategoryId(EmojiCategory.ID_FLAGS);
- }
- }
- } else {
- addShownCategoryId(EmojiCategory.ID_SYMBOLS);
- }
- addShownCategoryId(EmojiCategory.ID_EMOTICONS);
-
- DynamicGridKeyboard recentsKbd =
- getKeyboard(EmojiCategory.ID_RECENTS, 0 /* categoryPageId */);
- recentsKbd.loadRecentKeys(mCategoryKeyboardMap.values());
-
- mCurrentCategoryId = Settings.readLastShownEmojiCategoryId(mPrefs, defaultCategoryId);
- Log.i(TAG, "Last Emoji category id is " + mCurrentCategoryId);
- if (!isShownCategoryId(mCurrentCategoryId)) {
- Log.i(TAG, "Last emoji category " + mCurrentCategoryId +
- " is invalid, starting in " + defaultCategoryId);
- mCurrentCategoryId = defaultCategoryId;
- } else if (mCurrentCategoryId == EmojiCategory.ID_RECENTS &&
- recentsKbd.getSortedKeys().isEmpty()) {
- Log.i(TAG, "No recent emojis found, starting in category " + defaultCategoryId);
- mCurrentCategoryId = defaultCategoryId;
- }
- }
-
- private void addShownCategoryId(final int categoryId) {
- // Load a keyboard of categoryId
- getKeyboard(categoryId, 0 /* categoryPageId */);
- final CategoryProperties properties =
- new CategoryProperties(categoryId, getCategoryPageCount(categoryId));
- mShownCategories.add(properties);
- }
-
- private boolean isShownCategoryId(final int categoryId) {
- for (final CategoryProperties prop : mShownCategories) {
- if (prop.mCategoryId == categoryId) {
- return true;
- }
- }
- return false;
- }
-
- public static String getCategoryName(final int categoryId, final int categoryPageId) {
- return sCategoryName[categoryId] + "-" + categoryPageId;
- }
-
- public int getCategoryId(final String name) {
- final String[] strings = name.split("-");
- return mCategoryNameToIdMap.get(strings[0]);
- }
-
- public int getCategoryTabIcon(final int categoryId) {
- return mCategoryTabIconId[categoryId];
- }
-
- public String getAccessibilityDescription(final int categoryId) {
- return mRes.getString(sAccessibilityDescriptionResourceIdsForCategories[categoryId]);
- }
-
- public ArrayList<CategoryProperties> getShownCategories() {
- return mShownCategories;
- }
-
- public int getCurrentCategoryId() {
- return mCurrentCategoryId;
- }
-
- public int getCurrentCategoryPageSize() {
- return getCategoryPageSize(mCurrentCategoryId);
- }
-
- public int getCategoryPageSize(final int categoryId) {
- for (final CategoryProperties prop : mShownCategories) {
- if (prop.mCategoryId == categoryId) {
- return prop.mPageCount;
- }
- }
- Log.w(TAG, "Invalid category id: " + categoryId);
- // Should not reach here.
- return 0;
- }
-
- public void setCurrentCategoryId(final int categoryId) {
- mCurrentCategoryId = categoryId;
- Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId);
- }
-
- public void setCurrentCategoryPageId(final int id) {
- mCurrentCategoryPageId = id;
- }
-
- public int getCurrentCategoryPageId() {
- return mCurrentCategoryPageId;
- }
-
- public void saveLastTypedCategoryPage() {
- Settings.writeLastTypedEmojiCategoryPageId(
- mPrefs, mCurrentCategoryId, mCurrentCategoryPageId);
- }
-
- public boolean isInRecentTab() {
- return mCurrentCategoryId == EmojiCategory.ID_RECENTS;
- }
-
- public int getTabIdFromCategoryId(final int categoryId) {
- for (int i = 0; i < mShownCategories.size(); ++i) {
- if (mShownCategories.get(i).mCategoryId == categoryId) {
- return i;
- }
- }
- Log.w(TAG, "categoryId not found: " + categoryId);
- return 0;
- }
-
- // Returns the view pager's page position for the categoryId
- public int getPageIdFromCategoryId(final int categoryId) {
- final int lastSavedCategoryPageId =
- Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId);
- int sum = 0;
- for (int i = 0; i < mShownCategories.size(); ++i) {
- final CategoryProperties props = mShownCategories.get(i);
- if (props.mCategoryId == categoryId) {
- return sum + lastSavedCategoryPageId;
- }
- sum += props.mPageCount;
- }
- Log.w(TAG, "categoryId not found: " + categoryId);
- return 0;
- }
-
- public int getRecentTabId() {
- return getTabIdFromCategoryId(EmojiCategory.ID_RECENTS);
- }
-
- private int getCategoryPageCount(final int categoryId) {
- final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
- return (keyboard.getSortedKeys().size() - 1) / mMaxPageKeyCount + 1;
- }
-
- // Returns a pair of the category id and the category page id from the view pager's page
- // position. The category page id is numbered in each category. And the view page position
- // is the position of the current shown page in the view pager which contains all pages of
- // all categories.
- public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(final int position) {
- int sum = 0;
- for (final CategoryProperties properties : mShownCategories) {
- final int temp = sum;
- sum += properties.mPageCount;
- if (sum > position) {
- return new Pair<>(properties.mCategoryId, position - temp);
- }
- }
- return null;
- }
-
- // Returns a keyboard from the view pager's page position.
- public DynamicGridKeyboard getKeyboardFromPagePosition(final int position) {
- final Pair<Integer, Integer> categoryAndId =
- getCategoryIdAndPageIdFromPagePosition(position);
- if (categoryAndId != null) {
- return getKeyboard(categoryAndId.first, categoryAndId.second);
- }
- return null;
- }
-
- private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) {
- return (((long) categoryId) << Integer.SIZE) | id;
- }
-
- public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
- synchronized (mCategoryKeyboardMap) {
- final Long categoryKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id);
- if (mCategoryKeyboardMap.containsKey(categoryKeyboardMapKey)) {
- return mCategoryKeyboardMap.get(categoryKeyboardMapKey);
- }
-
- if (categoryId == EmojiCategory.ID_RECENTS) {
- final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
- mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- mMaxPageKeyCount, categoryId);
- mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd);
- return kbd;
- }
-
- final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
- final Key[][] sortedKeys = sortKeysIntoPages(
- keyboard.getSortedKeys(), mMaxPageKeyCount);
- for (int pageId = 0; pageId < sortedKeys.length; ++pageId) {
- final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
- mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- mMaxPageKeyCount, categoryId);
- for (final Key emojiKey : sortedKeys[pageId]) {
- if (emojiKey == null) {
- break;
- }
- tempKeyboard.addKeyLast(emojiKey);
- }
- mCategoryKeyboardMap.put(
- getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard);
- }
- return mCategoryKeyboardMap.get(categoryKeyboardMapKey);
- }
- }
-
- public int getTotalPageCountOfAllCategories() {
- int sum = 0;
- for (CategoryProperties properties : mShownCategories) {
- sum += properties.mPageCount;
- }
- return sum;
- }
-
- private static Comparator<Key> EMOJI_KEY_COMPARATOR = new Comparator<Key>() {
- @Override
- public int compare(final Key lhs, final Key rhs) {
- final Rect lHitBox = lhs.getHitBox();
- final Rect rHitBox = rhs.getHitBox();
- if (lHitBox.top < rHitBox.top) {
- return -1;
- } else if (lHitBox.top > rHitBox.top) {
- return 1;
- }
- if (lHitBox.left < rHitBox.left) {
- return -1;
- } else if (lHitBox.left > rHitBox.left) {
- return 1;
- }
- if (lhs.getCode() == rhs.getCode()) {
- return 0;
- }
- return lhs.getCode() < rhs.getCode() ? -1 : 1;
- }
- };
-
- private static Key[][] sortKeysIntoPages(final List<Key> inKeys, final int maxPageCount) {
- final ArrayList<Key> keys = new ArrayList<>(inKeys);
- Collections.sort(keys, EMOJI_KEY_COMPARATOR);
- final int pageCount = (keys.size() - 1) / maxPageCount + 1;
- final Key[][] retval = new Key[pageCount][maxPageCount];
- for (int i = 0; i < keys.size(); ++i) {
- retval[i / maxPageCount][i % maxPageCount] = keys.get(i);
- }
- return retval;
- }
-
- private static boolean canShowFlagEmoji() {
- Paint paint = new Paint();
- String switzerland = "\uD83C\uDDE8\uD83C\uDDED"; // U+1F1E8 U+1F1ED Flag for Switzerland
- try {
- return paint.hasGlyph(switzerland);
- } catch (NoSuchMethodError e) {
- // Compare display width of single-codepoint emoji to width of flag emoji to determine
- // whether flag is rendered as single glyph or two adjacent regional indicator symbols.
- float flagWidth = paint.measureText(switzerland);
- float standardWidth = paint.measureText("\uD83D\uDC27"); // U+1F427 Penguin
- return flagWidth < standardWidth * 1.25;
- // This assumes that a valid glyph for the flag emoji must be less than 1.25 times
- // the width of the penguin.
- }
- }
-
- private static boolean canShowUnicodeEightEmoji() {
- Paint paint = new Paint();
- String cheese = "\uD83E\uDDC0"; // U+1F9C0 Cheese wedge
- try {
- return paint.hasGlyph(cheese);
- } catch (NoSuchMethodError e) {
- float cheeseWidth = paint.measureText(cheese);
- float tofuWidth = paint.measureText("\uFFFE");
- return cheeseWidth > tofuWidth;
- // This assumes that a valid glyph for the cheese wedge must be greater than the width
- // of the noncharacter.
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategoryPageIndicatorView.java
deleted file mode 100644
index 43d62c71a..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategoryPageIndicatorView.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-public final class EmojiCategoryPageIndicatorView extends View {
- private static final float BOTTOM_MARGIN_RATIO = 1.0f;
- private final Paint mPaint = new Paint();
- private int mCategoryPageSize = 0;
- private int mCurrentCategoryPageId = 0;
- private float mOffset = 0.0f;
-
- public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs,
- final int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setColors(final int foregroundColor, final int backgroundColor) {
- mPaint.setColor(foregroundColor);
- setBackgroundColor(backgroundColor);
- }
-
- public void setCategoryPageId(final int size, final int id, final float offset) {
- mCategoryPageSize = size;
- mCurrentCategoryPageId = id;
- mOffset = offset;
- invalidate();
- }
-
- @Override
- protected void onDraw(final Canvas canvas) {
- if (mCategoryPageSize <= 1) {
- // If the category is not set yet or contains only one category,
- // just clear and return.
- canvas.drawColor(0);
- return;
- }
- final float height = getHeight();
- final float width = getWidth();
- final float unitWidth = width / mCategoryPageSize;
- final float left = unitWidth * mCurrentCategoryPageId + mOffset * unitWidth;
- final float top = 0.0f;
- final float right = left + unitWidth;
- final float bottom = height * BOTTOM_MARGIN_RATIO;
- canvas.drawRect(left, top, right, bottom, mPaint);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiLayoutParams.java
deleted file mode 100644
index a85c3a97f..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiLayoutParams.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import android.content.Context;
-import android.content.res.Resources;
-import androidx.viewpager.widget.ViewPager;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-final class EmojiLayoutParams {
- private static final int DEFAULT_KEYBOARD_ROWS = 4;
-
- public final int mEmojiPagerHeight;
- private final int mEmojiPagerBottomMargin;
- public final int mEmojiKeyboardHeight;
- private final int mEmojiCategoryPageIdViewHeight;
- public final int mEmojiActionBarHeight;
- public final int mKeyVerticalGap;
- private final int mKeyHorizontalGap;
- private final int mBottomPadding;
- private final int mTopPadding;
-
- public EmojiLayoutParams(final Context context) {
- final Resources res = context.getResources();
- final int defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
- final int defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(context);
- mKeyVerticalGap = (int) res.getFraction(R.fraction.config_key_vertical_gap_holo,
- defaultKeyboardHeight, defaultKeyboardHeight);
- mBottomPadding = (int) res.getFraction(R.fraction.config_keyboard_bottom_padding_holo,
- defaultKeyboardHeight, defaultKeyboardHeight);
- mTopPadding = (int) res.getFraction(R.fraction.config_keyboard_top_padding_holo,
- defaultKeyboardHeight, defaultKeyboardHeight);
- mKeyHorizontalGap = (int) (res.getFraction(R.fraction.config_key_horizontal_gap_holo,
- defaultKeyboardWidth, defaultKeyboardWidth));
- mEmojiCategoryPageIdViewHeight =
- (int) (res.getDimension(R.dimen.config_emoji_category_page_id_height));
- final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding
- + mKeyVerticalGap;
- mEmojiActionBarHeight = baseheight / DEFAULT_KEYBOARD_ROWS
- - (mKeyVerticalGap - mBottomPadding) / 2;
- mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight
- - mEmojiCategoryPageIdViewHeight;
- mEmojiPagerBottomMargin = 0;
- mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1;
- }
-
- public void setPagerProperties(final ViewPager vp) {
- final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams();
- lp.height = mEmojiKeyboardHeight;
- lp.bottomMargin = mEmojiPagerBottomMargin;
- vp.setLayoutParams(lp);
- }
-
- public void setCategoryPageIdViewProperties(final View v) {
- final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams();
- lp.height = mEmojiCategoryPageIdViewHeight;
- v.setLayoutParams(lp);
- }
-
- public int getActionBarHeight() {
- return mEmojiActionBarHeight - mBottomPadding;
- }
-
- public void setActionBarProperties(final LinearLayout ll) {
- final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
- lp.height = getActionBarHeight();
- ll.setLayoutParams(lp);
- }
-
- public void setKeyProperties(final View v) {
- final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams();
- lp.leftMargin = mKeyHorizontalGap / 2;
- lp.rightMargin = mKeyHorizontalGap / 2;
- v.setLayoutParams(lp);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
deleted file mode 100644
index 09313f811..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.accessibility.KeyboardAccessibilityDelegate;
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.latin.R;
-
-/**
- * This is an extended {@link KeyboardView} class that hosts an emoji page keyboard.
- * Multi-touch unsupported. No gesture support.
- */
-// TODO: Implement key popup preview.
-final class EmojiPageKeyboardView extends KeyboardView implements
- GestureDetector.OnGestureListener {
- private static final long KEY_PRESS_DELAY_TIME = 250; // msec
- private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
-
- public interface OnKeyEventListener {
- public void onPressKey(Key key);
- public void onReleaseKey(Key key);
- }
-
- private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
- @Override
- public void onPressKey(final Key key) {}
- @Override
- public void onReleaseKey(final Key key) {}
- };
-
- private OnKeyEventListener mListener = EMPTY_LISTENER;
- private final KeyDetector mKeyDetector = new KeyDetector();
- private final GestureDetector mGestureDetector;
- private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
-
- public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.keyboardViewStyle);
- }
-
- public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
- final int defStyle) {
- super(context, attrs, defStyle);
- mGestureDetector = new GestureDetector(context, this);
- mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
- mHandler = new Handler();
- }
-
- public void setOnKeyEventListener(final OnKeyEventListener listener) {
- mListener = listener;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setKeyboard(final Keyboard keyboard) {
- super.setKeyboard(keyboard);
- mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
- if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- if (mAccessibilityDelegate == null) {
- mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector);
- }
- mAccessibilityDelegate.setKeyboard(keyboard);
- } else {
- mAccessibilityDelegate = null;
- }
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
- // Don't populate accessibility event with all Emoji keys.
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onHoverEvent(final MotionEvent event) {
- final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate =
- mAccessibilityDelegate;
- if (accessibilityDelegate != null
- && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return accessibilityDelegate.onHoverEvent(event);
- }
- return super.onHoverEvent(event);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onTouchEvent(final MotionEvent e) {
- if (mGestureDetector.onTouchEvent(e)) {
- return true;
- }
- final Key key = getKey(e);
- if (key != null && key != mCurrentKey) {
- releaseCurrentKey(false /* withKeyRegistering */);
- }
- return true;
- }
-
- // {@link GestureEnabler#OnGestureListener} methods.
- private Key mCurrentKey;
- private Runnable mPendingKeyDown;
- private final Handler mHandler;
-
- private Key getKey(final MotionEvent e) {
- final int index = e.getActionIndex();
- final int x = (int)e.getX(index);
- final int y = (int)e.getY(index);
- return mKeyDetector.detectHitKey(x, y);
- }
-
- void callListenerOnReleaseKey(final Key releasedKey, final boolean withKeyRegistering) {
- releasedKey.onReleased();
- invalidateKey(releasedKey);
- if (withKeyRegistering) {
- mListener.onReleaseKey(releasedKey);
- }
- }
-
- void callListenerOnPressKey(final Key pressedKey) {
- mPendingKeyDown = null;
- pressedKey.onPressed();
- invalidateKey(pressedKey);
- mListener.onPressKey(pressedKey);
- }
-
- public void releaseCurrentKey(final boolean withKeyRegistering) {
- mHandler.removeCallbacks(mPendingKeyDown);
- mPendingKeyDown = null;
- final Key currentKey = mCurrentKey;
- if (currentKey == null) {
- return;
- }
- callListenerOnReleaseKey(currentKey, withKeyRegistering);
- mCurrentKey = null;
- }
-
- @Override
- public boolean onDown(final MotionEvent e) {
- final Key key = getKey(e);
- releaseCurrentKey(false /* withKeyRegistering */);
- mCurrentKey = key;
- if (key == null) {
- return false;
- }
- // Do not trigger key-down effect right now in case this is actually a fling action.
- mPendingKeyDown = new Runnable() {
- @Override
- public void run() {
- callListenerOnPressKey(key);
- }
- };
- mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME);
- return false;
- }
-
- @Override
- public void onShowPress(final MotionEvent e) {
- // User feedback is done at {@link #onDown(MotionEvent)}.
- }
-
- @Override
- public boolean onSingleTapUp(final MotionEvent e) {
- final Key key = getKey(e);
- final Runnable pendingKeyDown = mPendingKeyDown;
- final Key currentKey = mCurrentKey;
- releaseCurrentKey(false /* withKeyRegistering */);
- if (key == null) {
- return false;
- }
- if (key == currentKey && pendingKeyDown != null) {
- pendingKeyDown.run();
- // Trigger key-release event a little later so that a user can see visual feedback.
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- callListenerOnReleaseKey(key, true /* withRegistering */);
- }
- }, KEY_RELEASE_DELAY_TIME);
- } else {
- callListenerOnReleaseKey(key, true /* withRegistering */);
- }
- return true;
- }
-
- @Override
- public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
- final float distanceY) {
- releaseCurrentKey(false /* withKeyRegistering */);
- return false;
- }
-
- @Override
- public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
- final float velocityY) {
- releaseCurrentKey(false /* withKeyRegistering */);
- return false;
- }
-
- @Override
- public void onLongPress(final MotionEvent e) {
- // Long press detection of {@link #mGestureDetector} is disabled and not used.
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java
deleted file mode 100644
index 18b9c7e36..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import androidx.viewpager.widget.PagerAdapter;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.latin.R;
-
-final class EmojiPalettesAdapter extends PagerAdapter {
- private static final String TAG = EmojiPalettesAdapter.class.getSimpleName();
- private static final boolean DEBUG_PAGER = false;
-
- private final EmojiPageKeyboardView.OnKeyEventListener mListener;
- private final DynamicGridKeyboard mRecentsKeyboard;
- private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = new SparseArray<>();
- private final EmojiCategory mEmojiCategory;
- private int mActivePosition = 0;
-
- public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
- final EmojiPageKeyboardView.OnKeyEventListener listener) {
- mEmojiCategory = emojiCategory;
- mListener = listener;
- mRecentsKeyboard = mEmojiCategory.getKeyboard(EmojiCategory.ID_RECENTS, 0);
- }
-
- public void flushPendingRecentKeys() {
- mRecentsKeyboard.flushPendingRecentKeys();
- final KeyboardView recentKeyboardView =
- mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
- if (recentKeyboardView != null) {
- recentKeyboardView.invalidateAllKeys();
- }
- }
-
- public void addRecentKey(final Key key) {
- if (mEmojiCategory.isInRecentTab()) {
- mRecentsKeyboard.addPendingKey(key);
- return;
- }
- mRecentsKeyboard.addKeyFirst(key);
- final KeyboardView recentKeyboardView =
- mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
- if (recentKeyboardView != null) {
- recentKeyboardView.invalidateAllKeys();
- }
- }
-
- public void onPageScrolled() {
- releaseCurrentKey(false /* withKeyRegistering */);
- }
-
- public void releaseCurrentKey(final boolean withKeyRegistering) {
- // Make sure the delayed key-down event (highlight effect and haptic feedback) will be
- // canceled.
- final EmojiPageKeyboardView currentKeyboardView =
- mActiveKeyboardViews.get(mActivePosition);
- if (currentKeyboardView == null) {
- return;
- }
- currentKeyboardView.releaseCurrentKey(withKeyRegistering);
- }
-
- @Override
- public int getCount() {
- return mEmojiCategory.getTotalPageCountOfAllCategories();
- }
-
- @Override
- public void setPrimaryItem(final ViewGroup container, final int position,
- final Object object) {
- if (mActivePosition == position) {
- return;
- }
- final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
- if (oldKeyboardView != null) {
- oldKeyboardView.releaseCurrentKey(false /* withKeyRegistering */);
- oldKeyboardView.deallocateMemory();
- }
- mActivePosition = position;
- }
-
- @Override
- public Object instantiateItem(final ViewGroup container, final int position) {
- if (DEBUG_PAGER) {
- Log.d(TAG, "instantiate item: " + position);
- }
- final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
- if (oldKeyboardView != null) {
- oldKeyboardView.deallocateMemory();
- // This may be redundant but wanted to be safer..
- mActiveKeyboardViews.remove(position);
- }
- final Keyboard keyboard =
- mEmojiCategory.getKeyboardFromPagePosition(position);
- final LayoutInflater inflater = LayoutInflater.from(container.getContext());
- final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
- R.layout.emoji_keyboard_page, container, false /* attachToRoot */);
- keyboardView.setKeyboard(keyboard);
- keyboardView.setOnKeyEventListener(mListener);
- container.addView(keyboardView);
- mActiveKeyboardViews.put(position, keyboardView);
- return keyboardView;
- }
-
- @Override
- public boolean isViewFromObject(final View view, final Object object) {
- return view == object;
- }
-
- @Override
- public void destroyItem(final ViewGroup container, final int position,
- final Object object) {
- if (DEBUG_PAGER) {
- Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName());
- }
- final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position);
- if (keyboardView != null) {
- keyboardView.deallocateMemory();
- mActiveKeyboardViews.remove(position);
- }
- if (object instanceof View) {
- container.removeView((View)object);
- } else {
- Log.w(TAG, "Warning!!! Emoji palette may be leaking. " + object);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
deleted file mode 100644
index 898605019..000000000
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.emoji;
-
-import static com.android.inputmethod.latin.common.Constants.NOT_A_COORDINATE;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.preference.PreferenceManager;
-import androidx.viewpager.widget.ViewPager;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TabHost;
-import android.widget.TabHost.OnTabChangeListener;
-import android.widget.TabWidget;
-import android.widget.TextView;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
-import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.internal.KeyDrawParams;
-import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
-import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-/**
- * View class to implement Emoji palettes.
- * The Emoji keyboard consists of group of views layout/emoji_palettes_view.
- * <ol>
- * <li> Emoji category tabs.
- * <li> Delete button.
- * <li> Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab.
- * <li> Back to main keyboard button and enter button.
- * </ol>
- * Because of the above reasons, this class doesn't extend {@link KeyboardView}.
- */
-public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
- ViewPager.OnPageChangeListener, View.OnClickListener, View.OnTouchListener,
- EmojiPageKeyboardView.OnKeyEventListener {
- private final int mFunctionalKeyBackgroundId;
- private final int mSpacebarBackgroundId;
- private final boolean mCategoryIndicatorEnabled;
- private final int mCategoryIndicatorDrawableResId;
- private final int mCategoryIndicatorBackgroundResId;
- private final int mCategoryPageIndicatorColor;
- private final int mCategoryPageIndicatorBackground;
- private EmojiPalettesAdapter mEmojiPalettesAdapter;
- private final EmojiLayoutParams mEmojiLayoutParams;
- private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener;
-
- private ImageButton mDeleteKey;
- private TextView mAlphabetKeyLeft;
- private TextView mAlphabetKeyRight;
- private View mSpacebar;
- // TODO: Remove this workaround.
- private View mSpacebarIcon;
- private TabHost mTabHost;
- private ViewPager mEmojiPager;
- private int mCurrentPagerPosition = 0;
- private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
-
- private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
-
- private final EmojiCategory mEmojiCategory;
-
- public EmojiPalettesView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.emojiPalettesViewStyle);
- }
-
- public EmojiPalettesView(final Context context, final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
- final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
- R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
- final int keyBackgroundId = keyboardViewAttr.getResourceId(
- R.styleable.KeyboardView_keyBackground, 0);
- mFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId(
- R.styleable.KeyboardView_functionalKeyBackground, keyBackgroundId);
- mSpacebarBackgroundId = keyboardViewAttr.getResourceId(
- R.styleable.KeyboardView_spacebarBackground, keyBackgroundId);
- keyboardViewAttr.recycle();
- final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
- context, null /* editorInfo */);
- final Resources res = context.getResources();
- mEmojiLayoutParams = new EmojiLayoutParams(context);
- builder.setSubtype(RichInputMethodSubtype.getEmojiSubtype());
- builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(context),
- mEmojiLayoutParams.mEmojiKeyboardHeight);
- final KeyboardLayoutSet layoutSet = builder.build();
- final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs,
- R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView);
- mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context),
- res, layoutSet, emojiPalettesViewAttr);
- mCategoryIndicatorEnabled = emojiPalettesViewAttr.getBoolean(
- R.styleable.EmojiPalettesView_categoryIndicatorEnabled, false);
- mCategoryIndicatorDrawableResId = emojiPalettesViewAttr.getResourceId(
- R.styleable.EmojiPalettesView_categoryIndicatorDrawable, 0);
- mCategoryIndicatorBackgroundResId = emojiPalettesViewAttr.getResourceId(
- R.styleable.EmojiPalettesView_categoryIndicatorBackground, 0);
- mCategoryPageIndicatorColor = emojiPalettesViewAttr.getColor(
- R.styleable.EmojiPalettesView_categoryPageIndicatorColor, 0);
- mCategoryPageIndicatorBackground = emojiPalettesViewAttr.getColor(
- R.styleable.EmojiPalettesView_categoryPageIndicatorBackground, 0);
- emojiPalettesViewAttr.recycle();
- mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener();
- }
-
- @Override
- protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- final Resources res = getContext().getResources();
- // The main keyboard expands to the entire this {@link KeyboardView}.
- final int width = ResourceUtils.getDefaultKeyboardWidth(getContext())
- + getPaddingLeft() + getPaddingRight();
- final int height = ResourceUtils.getDefaultKeyboardHeight(res)
- + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height)
- + getPaddingTop() + getPaddingBottom();
- setMeasuredDimension(width, height);
- }
-
- private void addTab(final TabHost host, final int categoryId) {
- final String tabId = EmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */);
- final TabHost.TabSpec tspec = host.newTabSpec(tabId);
- tspec.setContent(R.id.emoji_keyboard_dummy);
- final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate(
- R.layout.emoji_keyboard_tab_icon, null);
- // TODO: Replace background color with its own setting rather than using the
- // category page indicator background as a workaround.
- iconView.setBackgroundColor(mCategoryPageIndicatorBackground);
- iconView.setImageResource(mEmojiCategory.getCategoryTabIcon(categoryId));
- iconView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId));
- tspec.setIndicator(iconView);
- host.addTab(tspec);
- }
-
- @Override
- protected void onFinishInflate() {
- mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost);
- mTabHost.setup();
- for (final EmojiCategory.CategoryProperties properties
- : mEmojiCategory.getShownCategories()) {
- addTab(mTabHost, properties.mCategoryId);
- }
- mTabHost.setOnTabChangedListener(this);
- final TabWidget tabWidget = mTabHost.getTabWidget();
- tabWidget.setStripEnabled(mCategoryIndicatorEnabled);
- if (mCategoryIndicatorEnabled) {
- // On TabWidget's strip, what looks like an indicator is actually a background.
- // And what looks like a background are actually left and right drawables.
- tabWidget.setBackgroundResource(mCategoryIndicatorDrawableResId);
- tabWidget.setLeftStripDrawable(mCategoryIndicatorBackgroundResId);
- tabWidget.setRightStripDrawable(mCategoryIndicatorBackgroundResId);
- }
-
- mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this);
-
- mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager);
- mEmojiPager.setAdapter(mEmojiPalettesAdapter);
- mEmojiPager.setOnPageChangeListener(this);
- mEmojiPager.setOffscreenPageLimit(0);
- mEmojiPager.setPersistentDrawingCache(PERSISTENT_NO_CACHE);
- mEmojiLayoutParams.setPagerProperties(mEmojiPager);
-
- mEmojiCategoryPageIndicatorView =
- (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view);
- mEmojiCategoryPageIndicatorView.setColors(
- mCategoryPageIndicatorColor, mCategoryPageIndicatorBackground);
- mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
-
- setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */);
-
- final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar);
- mEmojiLayoutParams.setActionBarProperties(actionBar);
-
- // deleteKey depends only on OnTouchListener.
- mDeleteKey = (ImageButton)findViewById(R.id.emoji_keyboard_delete);
- mDeleteKey.setBackgroundResource(mFunctionalKeyBackgroundId);
- mDeleteKey.setTag(Constants.CODE_DELETE);
- mDeleteKey.setOnTouchListener(mDeleteKeyOnTouchListener);
-
- // {@link #mAlphabetKeyLeft}, {@link #mAlphabetKeyRight, and spaceKey depend on
- // {@link View.OnClickListener} as well as {@link View.OnTouchListener}.
- // {@link View.OnTouchListener} is used as the trigger of key-press, while
- // {@link View.OnClickListener} is used as the trigger of key-release which does not occur
- // if the event is canceled by moving off the finger from the view.
- // The text on alphabet keys are set at
- // {@link #startEmojiPalettes(String,int,float,Typeface)}.
- mAlphabetKeyLeft = (TextView)findViewById(R.id.emoji_keyboard_alphabet_left);
- mAlphabetKeyLeft.setBackgroundResource(mFunctionalKeyBackgroundId);
- mAlphabetKeyLeft.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
- mAlphabetKeyLeft.setOnTouchListener(this);
- mAlphabetKeyLeft.setOnClickListener(this);
- mAlphabetKeyRight = (TextView)findViewById(R.id.emoji_keyboard_alphabet_right);
- mAlphabetKeyRight.setBackgroundResource(mFunctionalKeyBackgroundId);
- mAlphabetKeyRight.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
- mAlphabetKeyRight.setOnTouchListener(this);
- mAlphabetKeyRight.setOnClickListener(this);
- mSpacebar = findViewById(R.id.emoji_keyboard_space);
- mSpacebar.setBackgroundResource(mSpacebarBackgroundId);
- mSpacebar.setTag(Constants.CODE_SPACE);
- mSpacebar.setOnTouchListener(this);
- mSpacebar.setOnClickListener(this);
- mEmojiLayoutParams.setKeyProperties(mSpacebar);
- mSpacebarIcon = findViewById(R.id.emoji_keyboard_space_icon);
- }
-
- @Override
- public boolean dispatchTouchEvent(final MotionEvent ev) {
- // Add here to the stack trace to nail down the {@link IllegalArgumentException} exception
- // in MotionEvent that sporadically happens.
- // TODO: Remove this override method once the issue has been addressed.
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public void onTabChanged(final String tabId) {
- AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
- Constants.CODE_UNSPECIFIED, this);
- final int categoryId = mEmojiCategory.getCategoryId(tabId);
- setCurrentCategoryId(categoryId, false /* force */);
- updateEmojiCategoryPageIdView();
- }
-
- @Override
- public void onPageSelected(final int position) {
- final Pair<Integer, Integer> newPos =
- mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position);
- setCurrentCategoryId(newPos.first /* categoryId */, false /* force */);
- mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */);
- updateEmojiCategoryPageIdView();
- mCurrentPagerPosition = position;
- }
-
- @Override
- public void onPageScrollStateChanged(final int state) {
- // Ignore this message. Only want the actual page selected.
- }
-
- @Override
- public void onPageScrolled(final int position, final float positionOffset,
- final int positionOffsetPixels) {
- mEmojiPalettesAdapter.onPageScrolled();
- final Pair<Integer, Integer> newPos =
- mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position);
- final int newCategoryId = newPos.first;
- final int newCategorySize = mEmojiCategory.getCategoryPageSize(newCategoryId);
- final int currentCategoryId = mEmojiCategory.getCurrentCategoryId();
- final int currentCategoryPageId = mEmojiCategory.getCurrentCategoryPageId();
- final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageSize();
- if (newCategoryId == currentCategoryId) {
- mEmojiCategoryPageIndicatorView.setCategoryPageId(
- newCategorySize, newPos.second, positionOffset);
- } else if (newCategoryId > currentCategoryId) {
- mEmojiCategoryPageIndicatorView.setCategoryPageId(
- currentCategorySize, currentCategoryPageId, positionOffset);
- } else if (newCategoryId < currentCategoryId) {
- mEmojiCategoryPageIndicatorView.setCategoryPageId(
- currentCategorySize, currentCategoryPageId, positionOffset - 1);
- }
- }
-
- /**
- * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnTouchListener}
- * interface to handle touch events from View-based elements such as the space bar.
- * Note that this method is used only for observing {@link MotionEvent#ACTION_DOWN} to trigger
- * {@link KeyboardActionListener#onPressKey}. {@link KeyboardActionListener#onReleaseKey} will
- * be covered by {@link #onClick} as long as the event is not canceled.
- */
- @Override
- public boolean onTouch(final View v, final MotionEvent event) {
- if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
- return false;
- }
- final Object tag = v.getTag();
- if (!(tag instanceof Integer)) {
- return false;
- }
- final int code = (Integer) tag;
- mKeyboardActionListener.onPressKey(
- code, 0 /* repeatCount */, true /* isSinglePointer */);
- // It's important to return false here. Otherwise, {@link #onClick} and touch-down visual
- // feedback stop working.
- return false;
- }
-
- /**
- * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnClickListener}
- * interface to handle non-canceled touch-up events from View-based elements such as the space
- * bar.
- */
- @Override
- public void onClick(View v) {
- final Object tag = v.getTag();
- if (!(tag instanceof Integer)) {
- return;
- }
- final int code = (Integer) tag;
- mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE,
- false /* isKeyRepeat */);
- mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
- }
-
- /**
- * Called from {@link EmojiPageKeyboardView} through
- * {@link com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener}
- * interface to handle touch events from non-View-based elements such as Emoji buttons.
- */
- @Override
- public void onPressKey(final Key key) {
- final int code = key.getCode();
- mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */);
- }
-
- /**
- * Called from {@link EmojiPageKeyboardView} through
- * {@link com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener}
- * interface to handle touch events from non-View-based elements such as Emoji buttons.
- */
- @Override
- public void onReleaseKey(final Key key) {
- mEmojiPalettesAdapter.addRecentKey(key);
- mEmojiCategory.saveLastTypedCategoryPage();
- final int code = key.getCode();
- if (code == Constants.CODE_OUTPUT_TEXT) {
- mKeyboardActionListener.onTextInput(key.getOutputText());
- } else {
- mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE,
- false /* isKeyRepeat */);
- }
- mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
- }
-
- public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
- if (!enabled) return;
- // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
- setLayerType(LAYER_TYPE_HARDWARE, null);
- }
-
- private static void setupAlphabetKey(final TextView alphabetKey, final String label,
- final KeyDrawParams params) {
- alphabetKey.setText(label);
- alphabetKey.setTextColor(params.mFunctionalTextColor);
- alphabetKey.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mLabelSize);
- alphabetKey.setTypeface(params.mTypeface);
- }
-
- public void startEmojiPalettes(final String switchToAlphaLabel,
- final KeyVisualAttributes keyVisualAttr,
- final KeyboardIconsSet iconSet) {
- final int deleteIconResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_DELETE_KEY);
- if (deleteIconResId != 0) {
- mDeleteKey.setImageResource(deleteIconResId);
- }
- final int spacebarResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_SPACE_KEY);
- if (spacebarResId != 0) {
- // TODO: Remove this workaround to place the spacebar icon.
- mSpacebarIcon.setBackgroundResource(spacebarResId);
- }
- final KeyDrawParams params = new KeyDrawParams();
- params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr);
- setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params);
- setupAlphabetKey(mAlphabetKeyRight, switchToAlphaLabel, params);
- mEmojiPager.setAdapter(mEmojiPalettesAdapter);
- mEmojiPager.setCurrentItem(mCurrentPagerPosition);
- }
-
- public void stopEmojiPalettes() {
- mEmojiPalettesAdapter.releaseCurrentKey(true /* withKeyRegistering */);
- mEmojiPalettesAdapter.flushPendingRecentKeys();
- mEmojiPager.setAdapter(null);
- }
-
- public void setKeyboardActionListener(final KeyboardActionListener listener) {
- mKeyboardActionListener = listener;
- mDeleteKeyOnTouchListener.setKeyboardActionListener(listener);
- }
-
- private void updateEmojiCategoryPageIdView() {
- if (mEmojiCategoryPageIndicatorView == null) {
- return;
- }
- mEmojiCategoryPageIndicatorView.setCategoryPageId(
- mEmojiCategory.getCurrentCategoryPageSize(),
- mEmojiCategory.getCurrentCategoryPageId(), 0.0f /* offset */);
- }
-
- private void setCurrentCategoryId(final int categoryId, final boolean force) {
- final int oldCategoryId = mEmojiCategory.getCurrentCategoryId();
- if (oldCategoryId == categoryId && !force) {
- return;
- }
-
- if (oldCategoryId == EmojiCategory.ID_RECENTS) {
- // Needs to save pending updates for recent keys when we get out of the recents
- // category because we don't want to move the recent emojis around while the user
- // is in the recents category.
- mEmojiPalettesAdapter.flushPendingRecentKeys();
- }
-
- mEmojiCategory.setCurrentCategoryId(categoryId);
- final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId);
- final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId);
- if (force || mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(
- mEmojiPager.getCurrentItem()).first != categoryId) {
- mEmojiPager.setCurrentItem(newCategoryPageId, false /* smoothScroll */);
- }
- if (force || mTabHost.getCurrentTab() != newTabId) {
- mTabHost.setCurrentTab(newTabId);
- }
- }
-
- private static class DeleteKeyOnTouchListener implements OnTouchListener {
- private KeyboardActionListener mKeyboardActionListener =
- KeyboardActionListener.EMPTY_LISTENER;
-
- public void setKeyboardActionListener(final KeyboardActionListener listener) {
- mKeyboardActionListener = listener;
- }
-
- @Override
- public boolean onTouch(final View v, final MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- onTouchDown(v);
- return true;
- case MotionEvent.ACTION_MOVE:
- final float x = event.getX();
- final float y = event.getY();
- if (x < 0.0f || v.getWidth() < x || y < 0.0f || v.getHeight() < y) {
- // Stop generating key events once the finger moves away from the view area.
- onTouchCanceled(v);
- }
- return true;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- onTouchUp(v);
- return true;
- }
- return false;
- }
-
- private void onTouchDown(final View v) {
- mKeyboardActionListener.onPressKey(Constants.CODE_DELETE,
- 0 /* repeatCount */, true /* isSinglePointer */);
- v.setPressed(true /* pressed */);
- }
-
- private void onTouchUp(final View v) {
- mKeyboardActionListener.onCodeInput(Constants.CODE_DELETE,
- NOT_A_COORDINATE, NOT_A_COORDINATE, false /* isKeyRepeat */);
- mKeyboardActionListener.onReleaseKey(Constants.CODE_DELETE, false /* withSliding */);
- v.setPressed(false /* pressed */);
- }
-
- private void onTouchCanceled(final View v) {
- v.setBackgroundColor(Color.TRANSPARENT);
- }
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
deleted file mode 100644
index c76a9aca4..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.graphics.Canvas;
-import android.view.View;
-
-import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.keyboard.PointerTracker;
-
-import javax.annotation.Nonnull;
-
-/**
- * Abstract base class for previews that are drawn on DrawingPreviewPlacerView, e.g.,
- * GestureFloatingTextDrawingPreview, GestureTrailsDrawingPreview, and
- * SlidingKeyInputDrawingPreview.
- */
-public abstract class AbstractDrawingPreview {
- private View mDrawingView;
- private boolean mPreviewEnabled;
- private boolean mHasValidGeometry;
-
- public void setDrawingView(@Nonnull final DrawingPreviewPlacerView drawingView) {
- mDrawingView = drawingView;
- drawingView.addPreview(this);
- }
-
- protected void invalidateDrawingView() {
- if (mDrawingView != null) {
- mDrawingView.invalidate();
- }
- }
-
- protected final boolean isPreviewEnabled() {
- return mPreviewEnabled && mHasValidGeometry;
- }
-
- public final void setPreviewEnabled(final boolean enabled) {
- mPreviewEnabled = enabled;
- }
-
- /**
- * Set {@link MainKeyboardView} geometry and position in the window of input method.
- * The class that is overriding this method must call this super implementation.
- *
- * @param originCoords the top-left coordinates of the {@link MainKeyboardView} in
- * the input method window coordinate-system. This is unused but has a point in an
- * extended class, such as {@link GestureTrailsDrawingPreview}.
- * @param width the width of {@link MainKeyboardView}.
- * @param height the height of {@link MainKeyboardView}.
- */
- public void setKeyboardViewGeometry(@Nonnull final int[] originCoords, final int width,
- final int height) {
- mHasValidGeometry = (width > 0 && height > 0);
- }
-
- public abstract void onDeallocateMemory();
-
- /**
- * Draws the preview
- * @param canvas The canvas where the preview is drawn.
- */
- public abstract void drawPreview(@Nonnull final Canvas canvas);
-
- /**
- * Set the position of the preview.
- * @param tracker The new location of the preview is based on the points in PointerTracker.
- */
- public abstract void setPreviewPosition(@Nonnull final PointerTracker tracker);
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java b/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java
deleted file mode 100644
index 33f6b4965..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.Log;
-
-public final class AlphabetShiftState {
- private static final String TAG = AlphabetShiftState.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private static final int UNSHIFTED = 0;
- private static final int MANUAL_SHIFTED = 1;
- private static final int MANUAL_SHIFTED_FROM_AUTO = 2;
- private static final int AUTOMATIC_SHIFTED = 3;
- private static final int SHIFT_LOCKED = 4;
- private static final int SHIFT_LOCK_SHIFTED = 5;
-
- private int mState = UNSHIFTED;
-
- public void setShifted(boolean newShiftState) {
- final int oldState = mState;
- if (newShiftState) {
- switch (oldState) {
- case UNSHIFTED:
- mState = MANUAL_SHIFTED;
- break;
- case AUTOMATIC_SHIFTED:
- mState = MANUAL_SHIFTED_FROM_AUTO;
- break;
- case SHIFT_LOCKED:
- mState = SHIFT_LOCK_SHIFTED;
- break;
- }
- } else {
- switch (oldState) {
- case MANUAL_SHIFTED:
- case MANUAL_SHIFTED_FROM_AUTO:
- case AUTOMATIC_SHIFTED:
- mState = UNSHIFTED;
- break;
- case SHIFT_LOCK_SHIFTED:
- mState = SHIFT_LOCKED;
- break;
- }
- }
- if (DEBUG)
- Log.d(TAG, "setShifted(" + newShiftState + "): " + toString(oldState) + " > " + this);
- }
-
- public void setShiftLocked(boolean newShiftLockState) {
- final int oldState = mState;
- if (newShiftLockState) {
- switch (oldState) {
- case UNSHIFTED:
- case MANUAL_SHIFTED:
- case MANUAL_SHIFTED_FROM_AUTO:
- case AUTOMATIC_SHIFTED:
- mState = SHIFT_LOCKED;
- break;
- }
- } else {
- mState = UNSHIFTED;
- }
- if (DEBUG)
- Log.d(TAG, "setShiftLocked(" + newShiftLockState + "): " + toString(oldState)
- + " > " + this);
- }
-
- public void setAutomaticShifted() {
- final int oldState = mState;
- mState = AUTOMATIC_SHIFTED;
- if (DEBUG)
- Log.d(TAG, "setAutomaticShifted: " + toString(oldState) + " > " + this);
- }
-
- public boolean isShiftedOrShiftLocked() {
- return mState != UNSHIFTED;
- }
-
- public boolean isShiftLocked() {
- return mState == SHIFT_LOCKED || mState == SHIFT_LOCK_SHIFTED;
- }
-
- public boolean isShiftLockShifted() {
- return mState == SHIFT_LOCK_SHIFTED;
- }
-
- public boolean isAutomaticShifted() {
- return mState == AUTOMATIC_SHIFTED;
- }
-
- public boolean isManualShifted() {
- return mState == MANUAL_SHIFTED || mState == MANUAL_SHIFTED_FROM_AUTO
- || mState == SHIFT_LOCK_SHIFTED;
- }
-
- public boolean isManualShiftedFromAutomaticShifted() {
- return mState == MANUAL_SHIFTED_FROM_AUTO;
- }
-
- @Override
- public String toString() {
- return toString(mState);
- }
-
- private static String toString(int state) {
- switch (state) {
- case UNSHIFTED: return "UNSHIFTED";
- case MANUAL_SHIFTED: return "MANUAL_SHIFTED";
- case MANUAL_SHIFTED_FROM_AUTO: return "MANUAL_SHIFTED_FROM_AUTO";
- case AUTOMATIC_SHIFTED: return "AUTOMATIC_SHIFTED";
- case SHIFT_LOCKED: return "SHIFT_LOCKED";
- case SHIFT_LOCK_SHIFTED: return "SHIFT_LOCK_SHIFTED";
- default: return "UNKNOWN";
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java b/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java
deleted file mode 100644
index 77d0e7a90..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.InputPointers;
-
-/**
- * This class arbitrates batch input.
- * An instance of this class holds a {@link GestureStrokeRecognitionPoints}.
- * And it arbitrates multiple strokes gestured by multiple fingers and aggregates those gesture
- * points into one batch input.
- */
-public class BatchInputArbiter {
- public interface BatchInputArbiterListener {
- public void onStartBatchInput();
- public void onUpdateBatchInput(
- final InputPointers aggregatedPointers, final long moveEventTime);
- public void onStartUpdateBatchInputTimer();
- public void onEndBatchInput(final InputPointers aggregatedPointers, final long upEventTime);
- }
-
- // The starting time of the first stroke of a gesture input.
- private static long sGestureFirstDownTime;
- // The {@link InputPointers} that includes all events of a gesture input.
- private static final InputPointers sAggregatedPointers = new InputPointers(
- Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
- private static int sLastRecognitionPointSize = 0; // synchronized using sAggregatedPointers
- private static long sLastRecognitionTime = 0; // synchronized using sAggregatedPointers
-
- private final GestureStrokeRecognitionPoints mRecognitionPoints;
-
- public BatchInputArbiter(final int pointerId, final GestureStrokeRecognitionParams params) {
- mRecognitionPoints = new GestureStrokeRecognitionPoints(pointerId, params);
- }
-
- public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
- mRecognitionPoints.setKeyboardGeometry(keyWidth, keyboardHeight);
- }
-
- /**
- * Calculate elapsed time since the first gesture down.
- * @param eventTime the time of this event.
- * @return the elapsed time in millisecond from the first gesture down.
- */
- public int getElapsedTimeSinceFirstDown(final long eventTime) {
- return (int)(eventTime - sGestureFirstDownTime);
- }
-
- /**
- * Add a down event point.
- * @param x the x-coordinate of this down event.
- * @param y the y-coordinate of this down event.
- * @param downEventTime the time of this down event.
- * @param lastLetterTypingTime the last typing input time.
- * @param activePointerCount the number of active pointers when this pointer down event occurs.
- */
- public void addDownEventPoint(final int x, final int y, final long downEventTime,
- final long lastLetterTypingTime, final int activePointerCount) {
- if (activePointerCount == 1) {
- sGestureFirstDownTime = downEventTime;
- }
- final int elapsedTimeSinceFirstDown = getElapsedTimeSinceFirstDown(downEventTime);
- final int elapsedTimeSinceLastTyping = (int)(downEventTime - lastLetterTypingTime);
- mRecognitionPoints.addDownEventPoint(
- x, y, elapsedTimeSinceFirstDown, elapsedTimeSinceLastTyping);
- }
-
- /**
- * Add a move event point.
- * @param x the x-coordinate of this move event.
- * @param y the y-coordinate of this move event.
- * @param moveEventTime the time of this move event.
- * @param isMajorEvent false if this is a historical move event.
- * @param listener {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} of this
- * <code>listener</code> may be called if enough move points have been added.
- * @return true if this move event occurs on the valid gesture area.
- */
- public boolean addMoveEventPoint(final int x, final int y, final long moveEventTime,
- final boolean isMajorEvent, final BatchInputArbiterListener listener) {
- final int beforeLength = mRecognitionPoints.getLength();
- final boolean onValidArea = mRecognitionPoints.addEventPoint(
- x, y, getElapsedTimeSinceFirstDown(moveEventTime), isMajorEvent);
- if (mRecognitionPoints.getLength() > beforeLength) {
- listener.onStartUpdateBatchInputTimer();
- }
- return onValidArea;
- }
-
- /**
- * Determine whether the batch input has started or not.
- * @param listener {@link BatchInputArbiterListener#onStartBatchInput()} of this
- * <code>listener</code> will be called when the batch input has started successfully.
- * @return true if the batch input has started successfully.
- */
- public boolean mayStartBatchInput(final BatchInputArbiterListener listener) {
- if (!mRecognitionPoints.isStartOfAGesture()) {
- return false;
- }
- synchronized (sAggregatedPointers) {
- sAggregatedPointers.reset();
- sLastRecognitionPointSize = 0;
- sLastRecognitionTime = 0;
- listener.onStartBatchInput();
- }
- return true;
- }
-
- /**
- * Add synthetic move event point. After adding the point,
- * {@link #updateBatchInput(long,BatchInputArbiterListener)} will be called internally.
- * @param syntheticMoveEventTime the synthetic move event time.
- * @param listener the listener to be passed to
- * {@link #updateBatchInput(long,BatchInputArbiterListener)}.
- */
- public void updateBatchInputByTimer(final long syntheticMoveEventTime,
- final BatchInputArbiterListener listener) {
- mRecognitionPoints.duplicateLastPointWith(
- getElapsedTimeSinceFirstDown(syntheticMoveEventTime));
- updateBatchInput(syntheticMoveEventTime, listener);
- }
-
- /**
- * Determine whether we have enough gesture points to lookup dictionary.
- * @param moveEventTime the time of this move event.
- * @param listener {@link BatchInputArbiterListener#onUpdateBatchInput(InputPointers,long)} of
- * this <code>listener</code> will be called when enough event points we have. Also
- * {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} will be called to have
- * possible future synthetic move event.
- */
- public void updateBatchInput(final long moveEventTime,
- final BatchInputArbiterListener listener) {
- synchronized (sAggregatedPointers) {
- mRecognitionPoints.appendIncrementalBatchPoints(sAggregatedPointers);
- final int size = sAggregatedPointers.getPointerSize();
- if (size > sLastRecognitionPointSize && mRecognitionPoints.hasRecognitionTimePast(
- moveEventTime, sLastRecognitionTime)) {
- listener.onUpdateBatchInput(sAggregatedPointers, moveEventTime);
- listener.onStartUpdateBatchInputTimer();
- // The listener may change the size of the pointers (when auto-committing
- // for example), so we need to get the size from the pointers again.
- sLastRecognitionPointSize = sAggregatedPointers.getPointerSize();
- sLastRecognitionTime = moveEventTime;
- }
- }
- }
-
- /**
- * Determine whether the batch input has ended successfully or continues.
- * @param upEventTime the time of this up event.
- * @param activePointerCount the number of active pointers when this pointer up event occurs.
- * @param listener {@link BatchInputArbiterListener#onEndBatchInput(InputPointers,long)} of this
- * <code>listener</code> will be called when the batch input has started successfully.
- * @return true if the batch input has ended successfully.
- */
- public boolean mayEndBatchInput(final long upEventTime, final int activePointerCount,
- final BatchInputArbiterListener listener) {
- synchronized (sAggregatedPointers) {
- mRecognitionPoints.appendAllBatchPoints(sAggregatedPointers);
- if (activePointerCount == 1) {
- listener.onEndBatchInput(sAggregatedPointers, upEventTime);
- return true;
- }
- }
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java b/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java
deleted file mode 100644
index 4b355a4ab..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.Log;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.define.DebugFlags;
-
-// This hack is applied to certain classes of tablets.
-public final class BogusMoveEventDetector {
- private static final String TAG = BogusMoveEventDetector.class.getSimpleName();
- private static final boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED;
-
- // Move these thresholds to resource.
- // These thresholds' unit is a diagonal length of a key.
- private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
- private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
-
- private static boolean sNeedsProximateBogusDownMoveUpEventHack;
-
- public static void init(final Resources res) {
- // The proximate bogus down move up event hack is needed for a device such like,
- // 1) is large tablet, or 2) is small tablet and the screen density is less than hdpi.
- // Though it seems odd to use screen density as criteria of the quality of the touch
- // screen, the small table that has a less density screen than hdpi most likely has been
- // made with the touch screen that needs the hack.
- final int screenMetrics = res.getInteger(R.integer.config_screen_metrics);
- final boolean isLargeTablet = (screenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET);
- final boolean isSmallTablet = (screenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET);
- final int densityDpi = res.getDisplayMetrics().densityDpi;
- final boolean hasLowDensityScreen = (densityDpi < DisplayMetrics.DENSITY_HIGH);
- final boolean needsTheHack = isLargeTablet || (isSmallTablet && hasLowDensityScreen);
- if (DEBUG_MODE) {
- final int sw = res.getConfiguration().smallestScreenWidthDp;
- Log.d(TAG, "needsProximateBogusDownMoveUpEventHack=" + needsTheHack
- + " smallestScreenWidthDp=" + sw + " densityDpi=" + densityDpi
- + " screenMetrics=" + screenMetrics);
- }
- sNeedsProximateBogusDownMoveUpEventHack = needsTheHack;
- }
-
- private int mAccumulatedDistanceThreshold;
- private int mRadiusThreshold;
-
- // Accumulated distance from actual and artificial down keys.
- /* package */ int mAccumulatedDistanceFromDownKey;
- private int mActualDownX;
- private int mActualDownY;
-
- public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
- final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
- mAccumulatedDistanceThreshold = (int)(
- keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
- mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
- }
-
- public void onActualDownEvent(final int x, final int y) {
- mActualDownX = x;
- mActualDownY = y;
- }
-
- public void onDownKey() {
- mAccumulatedDistanceFromDownKey = 0;
- }
-
- public void onMoveKey(final int distance) {
- mAccumulatedDistanceFromDownKey += distance;
- }
-
- public boolean hasTraveledLongDistance(final int x, final int y) {
- if (!sNeedsProximateBogusDownMoveUpEventHack) {
- return false;
- }
- final int dx = Math.abs(x - mActualDownX);
- final int dy = Math.abs(y - mActualDownY);
- // A bogus move event should be a horizontal movement. A vertical movement might be
- // a sloppy typing and should be ignored.
- return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
- }
-
- public int getAccumulatedDistanceFromDownKey() {
- return mAccumulatedDistanceFromDownKey;
- }
-
- public int getDistanceFromDownEvent(final int x, final int y) {
- return getDistance(x, y, mActualDownX, mActualDownY);
- }
-
- private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
- return (int)Math.hypot(x1 - x2, y1 - y2);
- }
-
- public boolean isCloseToActualDownEvent(final int x, final int y) {
- return sNeedsProximateBogusDownMoveUpEventHack
- && getDistanceFromDownEvent(x, y) < mRadiusThreshold;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
deleted file mode 100644
index 2e2ed52dd..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import android.text.TextUtils;
-
-/**
- * The string parser of codesArray specification for <GridRows />. The attribute codesArray is an
- * array of string.
- * Each element of the array defines a key label by specifying a code point as a hexadecimal string.
- * A key label may consist of multiple code points separated by comma.
- * Each element of the array optionally can have an output text definition after vertical bar
- * marker. An output text may consist of multiple code points separated by comma.
- * The format of the codesArray element should be:
- * <pre>
- * label1[,label2]*(|outputText1[,outputText2]*(|minSupportSdkVersion)?)?
- * </pre>
- */
-// TODO: Write unit tests for this class.
-public final class CodesArrayParser {
- // Constants for parsing.
- private static final char COMMA = Constants.CODE_COMMA;
- private static final String COMMA_REGEX = StringUtils.newSingleCodePointString(COMMA);
- private static final String VERTICAL_BAR_REGEX = // "\\|"
- new String(new char[] { Constants.CODE_BACKSLASH, Constants.CODE_VERTICAL_BAR });
- private static final int BASE_HEX = 16;
-
- private CodesArrayParser() {
- // This utility class is not publicly instantiable.
- }
-
- private static String getLabelSpec(final String codesArraySpec) {
- final String[] strs = codesArraySpec.split(VERTICAL_BAR_REGEX, -1);
- if (strs.length <= 1) {
- return codesArraySpec;
- }
- return strs[0];
- }
-
- public static String parseLabel(final String codesArraySpec) {
- final String labelSpec = getLabelSpec(codesArraySpec);
- final StringBuilder sb = new StringBuilder();
- for (final String codeInHex : labelSpec.split(COMMA_REGEX)) {
- final int codePoint = Integer.parseInt(codeInHex, BASE_HEX);
- sb.appendCodePoint(codePoint);
- }
- return sb.toString();
- }
-
- private static String getCodeSpec(final String codesArraySpec) {
- final String[] strs = codesArraySpec.split(VERTICAL_BAR_REGEX, -1);
- if (strs.length <= 1) {
- return codesArraySpec;
- }
- return TextUtils.isEmpty(strs[1]) ? strs[0] : strs[1];
- }
-
- public static int getMinSupportSdkVersion(final String codesArraySpec) {
- final String[] strs = codesArraySpec.split(VERTICAL_BAR_REGEX, -1);
- if (strs.length <= 2) {
- return 0;
- }
- try {
- return Integer.parseInt(strs[2]);
- } catch (NumberFormatException e) {
- return 0;
- }
- }
-
- public static int parseCode(final String codesArraySpec) {
- final String codeSpec = getCodeSpec(codesArraySpec);
- if (codeSpec.indexOf(COMMA) < 0) {
- return Integer.parseInt(codeSpec, BASE_HEX);
- }
- return Constants.CODE_OUTPUT_TEXT;
- }
-
- public static String parseOutputText(final String codesArraySpec) {
- final String codeSpec = getCodeSpec(codesArraySpec);
- if (codeSpec.indexOf(COMMA) < 0) {
- return null;
- }
- final StringBuilder sb = new StringBuilder();
- for (final String codeInHex : codeSpec.split(COMMA_REGEX)) {
- final int codePoint = Integer.parseInt(codeInHex, BASE_HEX);
- sb.appendCodePoint(codePoint);
- }
- return sb.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
deleted file mode 100644
index 9c0d7436b..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.util.AttributeSet;
-import android.widget.RelativeLayout;
-
-import com.android.inputmethod.latin.common.CoordinateUtils;
-
-import java.util.ArrayList;
-
-public final class DrawingPreviewPlacerView extends RelativeLayout {
- private final int[] mKeyboardViewOrigin = CoordinateUtils.newInstance();
-
- private final ArrayList<AbstractDrawingPreview> mPreviews = new ArrayList<>();
-
- public DrawingPreviewPlacerView(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- setWillNotDraw(false);
- }
-
- public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
- if (!enabled) return;
- final Paint layerPaint = new Paint();
- layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
- setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
- }
-
- public void addPreview(final AbstractDrawingPreview preview) {
- if (mPreviews.indexOf(preview) < 0) {
- mPreviews.add(preview);
- }
- }
-
- public void setKeyboardViewGeometry(final int[] originCoords, final int width,
- final int height) {
- CoordinateUtils.copy(mKeyboardViewOrigin, originCoords);
- final int count = mPreviews.size();
- for (int i = 0; i < count; i++) {
- mPreviews.get(i).setKeyboardViewGeometry(originCoords, width, height);
- }
- }
-
- public void deallocateMemory() {
- final int count = mPreviews.size();
- for (int i = 0; i < count; i++) {
- mPreviews.get(i).onDeallocateMemory();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- deallocateMemory();
- }
-
- @Override
- public void onDraw(final Canvas canvas) {
- super.onDraw(canvas);
- final int originX = CoordinateUtils.x(mKeyboardViewOrigin);
- final int originY = CoordinateUtils.y(mKeyboardViewOrigin);
- canvas.translate(originX, originY);
- final int count = mPreviews.size();
- for (int i = 0; i < count; i++) {
- mPreviews.get(i).drawPreview(canvas);
- }
- canvas.translate(-originX, -originY);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java
deleted file mode 100644
index 06bdfc41b..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.MoreKeysPanel;
-import com.android.inputmethod.keyboard.PointerTracker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public interface DrawingProxy {
- /**
- * Called when a key is being pressed.
- * @param key the {@link Key} that is being pressed.
- * @param withPreview true if key popup preview should be displayed.
- */
- public void onKeyPressed(@Nonnull Key key, boolean withPreview);
-
- /**
- * Called when a key is being released.
- * @param key the {@link Key} that is being released.
- * @param withAnimation when true, key popup preview should be dismissed with animation.
- */
- public void onKeyReleased(@Nonnull Key key, boolean withAnimation);
-
- /**
- * Start showing more keys keyboard of a key that is being long pressed.
- * @param key the {@link Key} that is being long pressed and showing more keys keyboard.
- * @param tracker the {@link PointerTracker} that detects this long pressing.
- * @return {@link MoreKeysPanel} that is being shown. null if there is no need to show more keys
- * keyboard.
- */
- @Nullable
- public MoreKeysPanel showMoreKeysKeyboard(@Nonnull Key key, @Nonnull PointerTracker tracker);
-
- /**
- * Start a while-typing-animation.
- * @param fadeInOrOut {@link #FADE_IN} starts while-typing-fade-in animation.
- * {@link #FADE_OUT} starts while-typing-fade-out animation.
- */
- public void startWhileTypingAnimation(int fadeInOrOut);
- public static final int FADE_IN = 0;
- public static final int FADE_OUT = 1;
-
- /**
- * Show sliding-key input preview.
- * @param tracker the {@link PointerTracker} that is currently doing the sliding-key input.
- * null to dismiss the sliding-key input preview.
- */
- public void showSlidingKeyInputPreview(@Nullable PointerTracker tracker);
-
- /**
- * Show gesture trails.
- * @param tracker the {@link PointerTracker} whose gesture trail will be shown.
- * @param showsFloatingPreviewText when true, a gesture floating preview text will be shown
- * with this <code>tracker</code>'s trail.
- */
- public void showGestureTrail(@Nonnull PointerTracker tracker, boolean showsFloatingPreviewText);
-
- /**
- * Dismiss a gesture floating preview text without delay.
- */
- public void dismissGestureFloatingPreviewTextWithoutDelay();
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureEnabler.java b/java/src/com/android/inputmethod/keyboard/internal/GestureEnabler.java
deleted file mode 100644
index 7d14ae924..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureEnabler.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-
-public final class GestureEnabler {
- /** True if we should handle gesture events. */
- private boolean mShouldHandleGesture;
- private boolean mMainDictionaryAvailable;
- private boolean mGestureHandlingEnabledByInputField;
- private boolean mGestureHandlingEnabledByUser;
-
- private void updateGestureHandlingMode() {
- mShouldHandleGesture = mMainDictionaryAvailable
- && mGestureHandlingEnabledByInputField
- && mGestureHandlingEnabledByUser
- && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
- }
-
- // Note that this method is called from a non-UI thread.
- public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
- mMainDictionaryAvailable = mainDictionaryAvailable;
- updateGestureHandlingMode();
- }
-
- public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
- mGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
- updateGestureHandlingMode();
- }
-
- public void setPasswordMode(final boolean passwordMode) {
- mGestureHandlingEnabledByInputField = !passwordMode;
- updateGestureHandlingMode();
- }
-
- public boolean shouldHandleGesture() {
- return mShouldHandleGesture;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
deleted file mode 100644
index cb50b76ae..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.text.TextUtils;
-
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-
-import javax.annotation.Nonnull;
-
-/**
- * The class for single gesture preview text. The class for multiple gesture preview text will be
- * derived from it.
- *
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewTextSize
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewTextColor
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewTextOffset
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewColor
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewHorizontalPadding
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewVerticalPadding
- * @attr ref android.R.styleable#KeyboardView_gestureFloatingPreviewRoundRadius
- */
-public class GestureFloatingTextDrawingPreview extends AbstractDrawingPreview {
- protected static final class GesturePreviewTextParams {
- public final int mGesturePreviewTextOffset;
- public final int mGesturePreviewTextHeight;
- public final float mGesturePreviewHorizontalPadding;
- public final float mGesturePreviewVerticalPadding;
- public final float mGesturePreviewRoundRadius;
- public final int mDisplayWidth;
-
- private final int mGesturePreviewTextSize;
- private final int mGesturePreviewTextColor;
- private final int mGesturePreviewColor;
- private final Paint mPaint = new Paint();
-
- private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
-
- public GesturePreviewTextParams(final TypedArray mainKeyboardViewAttr) {
- mGesturePreviewTextSize = mainKeyboardViewAttr.getDimensionPixelSize(
- R.styleable.MainKeyboardView_gestureFloatingPreviewTextSize, 0);
- mGesturePreviewTextColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_gestureFloatingPreviewTextColor, 0);
- mGesturePreviewTextOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
- R.styleable.MainKeyboardView_gestureFloatingPreviewTextOffset, 0);
- mGesturePreviewColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_gestureFloatingPreviewColor, 0);
- mGesturePreviewHorizontalPadding = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_gestureFloatingPreviewHorizontalPadding, 0.0f);
- mGesturePreviewVerticalPadding = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f);
- mGesturePreviewRoundRadius = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
- mDisplayWidth = mainKeyboardViewAttr.getResources().getDisplayMetrics().widthPixels;
-
- final Paint textPaint = getTextPaint();
- final Rect textRect = new Rect();
- textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect);
- mGesturePreviewTextHeight = textRect.height();
- }
-
- public Paint getTextPaint() {
- mPaint.setAntiAlias(true);
- mPaint.setTextAlign(Align.CENTER);
- mPaint.setTextSize(mGesturePreviewTextSize);
- mPaint.setColor(mGesturePreviewTextColor);
- return mPaint;
- }
-
- public Paint getBackgroundPaint() {
- mPaint.setColor(mGesturePreviewColor);
- return mPaint;
- }
- }
-
- private final GesturePreviewTextParams mParams;
- private final RectF mGesturePreviewRectangle = new RectF();
- private int mPreviewTextX;
- private int mPreviewTextY;
- private SuggestedWords mSuggestedWords = SuggestedWords.getEmptyInstance();
- private final int[] mLastPointerCoords = CoordinateUtils.newInstance();
-
- public GestureFloatingTextDrawingPreview(final TypedArray mainKeyboardViewAttr) {
- mParams = new GesturePreviewTextParams(mainKeyboardViewAttr);
- }
-
- @Override
- public void onDeallocateMemory() {
- // Nothing to do here.
- }
-
- public void dismissGestureFloatingPreviewText() {
- setSuggetedWords(SuggestedWords.getEmptyInstance());
- }
-
- public void setSuggetedWords(@Nonnull final SuggestedWords suggestedWords) {
- if (!isPreviewEnabled()) {
- return;
- }
- mSuggestedWords = suggestedWords;
- updatePreviewPosition();
- }
-
- @Override
- public void setPreviewPosition(final PointerTracker tracker) {
- if (!isPreviewEnabled()) {
- return;
- }
- tracker.getLastCoordinates(mLastPointerCoords);
- updatePreviewPosition();
- }
-
- /**
- * Draws gesture preview text
- * @param canvas The canvas where preview text is drawn.
- */
- @Override
- public void drawPreview(final Canvas canvas) {
- if (!isPreviewEnabled() || mSuggestedWords.isEmpty()
- || TextUtils.isEmpty(mSuggestedWords.getWord(0))) {
- return;
- }
- final float round = mParams.mGesturePreviewRoundRadius;
- canvas.drawRoundRect(
- mGesturePreviewRectangle, round, round, mParams.getBackgroundPaint());
- final String text = mSuggestedWords.getWord(0);
- canvas.drawText(text, mPreviewTextX, mPreviewTextY, mParams.getTextPaint());
- }
-
- /**
- * Updates gesture preview text position based on mLastPointerCoords.
- */
- protected void updatePreviewPosition() {
- if (mSuggestedWords.isEmpty() || TextUtils.isEmpty(mSuggestedWords.getWord(0))) {
- invalidateDrawingView();
- return;
- }
- final String text = mSuggestedWords.getWord(0);
-
- final RectF rectangle = mGesturePreviewRectangle;
-
- final int textHeight = mParams.mGesturePreviewTextHeight;
- final float textWidth = mParams.getTextPaint().measureText(text);
- final float hPad = mParams.mGesturePreviewHorizontalPadding;
- final float vPad = mParams.mGesturePreviewVerticalPadding;
- final float rectWidth = textWidth + hPad * 2.0f;
- final float rectHeight = textHeight + vPad * 2.0f;
-
- final float rectX = Math.min(
- Math.max(CoordinateUtils.x(mLastPointerCoords) - rectWidth / 2.0f, 0.0f),
- mParams.mDisplayWidth - rectWidth);
- final float rectY = CoordinateUtils.y(mLastPointerCoords)
- - mParams.mGesturePreviewTextOffset - rectHeight;
- rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
-
- mPreviewTextX = (int)(rectX + hPad + textWidth / 2.0f);
- mPreviewTextY = (int)(rectY + vPad) + textHeight;
- // TODO: Should narrow the invalidate region.
- invalidateDrawingView();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingParams.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingParams.java
deleted file mode 100644
index eeba67892..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingParams.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-
-import com.android.inputmethod.latin.R;
-
-/**
- * This class holds parameters to control how a gesture stroke is sampled and drawn on the screen.
- *
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailMinSamplingDistance
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailMaxInterpolationAngularThreshold
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailMaxInterpolationDistanceThreshold
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailMaxInterpolationSegments
- */
-public final class GestureStrokeDrawingParams {
- public final double mMinSamplingDistance; // in pixel
- public final double mMaxInterpolationAngularThreshold; // in radian
- public final double mMaxInterpolationDistanceThreshold; // in pixel
- public final int mMaxInterpolationSegments;
-
- private static final float DEFAULT_MIN_SAMPLING_DISTANCE = 0.0f; // dp
- private static final int DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD = 15; // in degree
- private static final float DEFAULT_MAX_INTERPOLATION_DISTANCE_THRESHOLD = 0.0f; // dp
- private static final int DEFAULT_MAX_INTERPOLATION_SEGMENTS = 4;
-
- public GestureStrokeDrawingParams(final TypedArray mainKeyboardViewAttr) {
- mMinSamplingDistance = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_gestureTrailMinSamplingDistance,
- DEFAULT_MIN_SAMPLING_DISTANCE);
- final int interpolationAngularDegree = mainKeyboardViewAttr.getInteger(R.styleable
- .MainKeyboardView_gestureTrailMaxInterpolationAngularThreshold, 0);
- mMaxInterpolationAngularThreshold = (interpolationAngularDegree <= 0)
- ? Math.toRadians(DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD)
- : Math.toRadians(interpolationAngularDegree);
- mMaxInterpolationDistanceThreshold = mainKeyboardViewAttr.getDimension(R.styleable
- .MainKeyboardView_gestureTrailMaxInterpolationDistanceThreshold,
- DEFAULT_MAX_INTERPOLATION_DISTANCE_THRESHOLD);
- mMaxInterpolationSegments = mainKeyboardViewAttr.getInteger(
- R.styleable.MainKeyboardView_gestureTrailMaxInterpolationSegments,
- DEFAULT_MAX_INTERPOLATION_SEGMENTS);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java
deleted file mode 100644
index 07ef00924..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.latin.common.ResizableIntArray;
-
-/**
- * This class holds drawing points to represent a gesture stroke on the screen.
- */
-public final class GestureStrokeDrawingPoints {
- public static final int PREVIEW_CAPACITY = 256;
-
- private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
- private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
- private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
-
- private final GestureStrokeDrawingParams mDrawingParams;
-
- private int mStrokeId;
- private int mLastPreviewSize;
- private final HermiteInterpolator mInterpolator = new HermiteInterpolator();
- private int mLastInterpolatedPreviewIndex;
-
- private int mLastX;
- private int mLastY;
- private double mDistanceFromLastSample;
-
- public GestureStrokeDrawingPoints(final GestureStrokeDrawingParams drawingParams) {
- mDrawingParams = drawingParams;
- }
-
- private void reset() {
- mStrokeId++;
- mLastPreviewSize = 0;
- mLastInterpolatedPreviewIndex = 0;
- mPreviewEventTimes.setLength(0);
- mPreviewXCoordinates.setLength(0);
- mPreviewYCoordinates.setLength(0);
- }
-
- public int getGestureStrokeId() {
- return mStrokeId;
- }
-
- public void onDownEvent(final int x, final int y, final int elapsedTimeSinceFirstDown) {
- reset();
- onMoveEvent(x, y, elapsedTimeSinceFirstDown);
- }
-
- private boolean needsSampling(final int x, final int y) {
- mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY);
- mLastX = x;
- mLastY = y;
- final boolean isDownEvent = (mPreviewEventTimes.getLength() == 0);
- if (mDistanceFromLastSample >= mDrawingParams.mMinSamplingDistance || isDownEvent) {
- mDistanceFromLastSample = 0.0d;
- return true;
- }
- return false;
- }
-
- public void onMoveEvent(final int x, final int y, final int elapsedTimeSinceFirstDown) {
- if (needsSampling(x, y)) {
- mPreviewEventTimes.add(elapsedTimeSinceFirstDown);
- mPreviewXCoordinates.add(x);
- mPreviewYCoordinates.add(y);
- }
- }
-
- /**
- * Append sampled preview points.
- *
- * @param eventTimes the event time array of gesture trail to be drawn.
- * @param xCoords the x-coordinates array of gesture trail to be drawn.
- * @param yCoords the y-coordinates array of gesture trail to be drawn.
- * @param types the point types array of gesture trail. This is valid only when
- * {@link GestureTrailDrawingPoints#DEBUG_SHOW_POINTS} is true.
- */
- public void appendPreviewStroke(final ResizableIntArray eventTimes,
- final ResizableIntArray xCoords, final ResizableIntArray yCoords,
- final ResizableIntArray types) {
- final int length = mPreviewEventTimes.getLength() - mLastPreviewSize;
- if (length <= 0) {
- return;
- }
- eventTimes.append(mPreviewEventTimes, mLastPreviewSize, length);
- xCoords.append(mPreviewXCoordinates, mLastPreviewSize, length);
- yCoords.append(mPreviewYCoordinates, mLastPreviewSize, length);
- if (GestureTrailDrawingPoints.DEBUG_SHOW_POINTS) {
- types.fill(GestureTrailDrawingPoints.POINT_TYPE_SAMPLED, types.getLength(), length);
- }
- mLastPreviewSize = mPreviewEventTimes.getLength();
- }
-
- /**
- * Calculate interpolated points between the last interpolated point and the end of the trail.
- * And return the start index of the last interpolated segment of input arrays because it
- * may need to recalculate the interpolated points in the segment if further segments are
- * added to this stroke.
- *
- * @param lastInterpolatedIndex the start index of the last interpolated segment of
- * <code>eventTimes</code>, <code>xCoords</code>, and <code>yCoords</code>.
- * @param eventTimes the event time array of gesture trail to be drawn.
- * @param xCoords the x-coordinates array of gesture trail to be drawn.
- * @param yCoords the y-coordinates array of gesture trail to be drawn.
- * @param types the point types array of gesture trail. This is valid only when
- * {@link GestureTrailDrawingPoints#DEBUG_SHOW_POINTS} is true.
- * @return the start index of the last interpolated segment of input arrays.
- */
- public int interpolateStrokeAndReturnStartIndexOfLastSegment(final int lastInterpolatedIndex,
- final ResizableIntArray eventTimes, final ResizableIntArray xCoords,
- final ResizableIntArray yCoords, final ResizableIntArray types) {
- final int size = mPreviewEventTimes.getLength();
- final int[] pt = mPreviewEventTimes.getPrimitiveArray();
- final int[] px = mPreviewXCoordinates.getPrimitiveArray();
- final int[] py = mPreviewYCoordinates.getPrimitiveArray();
- mInterpolator.reset(px, py, 0, size);
- // The last segment of gesture stroke needs to be interpolated again because the slope of
- // the tangent at the last point isn't determined.
- int lastInterpolatedDrawIndex = lastInterpolatedIndex;
- int d1 = lastInterpolatedIndex;
- for (int p2 = mLastInterpolatedPreviewIndex + 1; p2 < size; p2++) {
- final int p1 = p2 - 1;
- final int p0 = p1 - 1;
- final int p3 = p2 + 1;
- mLastInterpolatedPreviewIndex = p1;
- lastInterpolatedDrawIndex = d1;
- mInterpolator.setInterval(p0, p1, p2, p3);
- final double m1 = Math.atan2(mInterpolator.mSlope1Y, mInterpolator.mSlope1X);
- final double m2 = Math.atan2(mInterpolator.mSlope2Y, mInterpolator.mSlope2X);
- final double deltaAngle = Math.abs(angularDiff(m2, m1));
- final int segmentsByAngle = (int)Math.ceil(
- deltaAngle / mDrawingParams.mMaxInterpolationAngularThreshold);
- final double deltaDistance = Math.hypot(mInterpolator.mP1X - mInterpolator.mP2X,
- mInterpolator.mP1Y - mInterpolator.mP2Y);
- final int segmentsByDistance = (int)Math.ceil(deltaDistance
- / mDrawingParams.mMaxInterpolationDistanceThreshold);
- final int segments = Math.min(mDrawingParams.mMaxInterpolationSegments,
- Math.max(segmentsByAngle, segmentsByDistance));
- final int t1 = eventTimes.get(d1);
- final int dt = pt[p2] - pt[p1];
- d1++;
- for (int i = 1; i < segments; i++) {
- final float t = i / (float)segments;
- mInterpolator.interpolate(t);
- eventTimes.addAt(d1, (int)(dt * t) + t1);
- xCoords.addAt(d1, (int)mInterpolator.mInterpolatedX);
- yCoords.addAt(d1, (int)mInterpolator.mInterpolatedY);
- if (GestureTrailDrawingPoints.DEBUG_SHOW_POINTS) {
- types.addAt(d1, GestureTrailDrawingPoints.POINT_TYPE_INTERPOLATED);
- }
- d1++;
- }
- eventTimes.addAt(d1, pt[p2]);
- xCoords.addAt(d1, px[p2]);
- yCoords.addAt(d1, py[p2]);
- if (GestureTrailDrawingPoints.DEBUG_SHOW_POINTS) {
- types.addAt(d1, GestureTrailDrawingPoints.POINT_TYPE_SAMPLED);
- }
- }
- return lastInterpolatedDrawIndex;
- }
-
- private static final double TWO_PI = Math.PI * 2.0d;
-
- /**
- * Calculate the angular of rotation from <code>a0</code> to <code>a1</code>.
- *
- * @param a1 the angular to which the rotation ends.
- * @param a0 the angular from which the rotation starts.
- * @return the angular rotation value from a0 to a1, normalized to [-PI, +PI].
- */
- private static double angularDiff(final double a1, final double a0) {
- double deltaAngle = a1 - a0;
- while (deltaAngle > Math.PI) {
- deltaAngle -= TWO_PI;
- }
- while (deltaAngle < -Math.PI) {
- deltaAngle += TWO_PI;
- }
- return deltaAngle;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionParams.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionParams.java
deleted file mode 100644
index e98729d43..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionParams.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-/**
- * This class holds parameters to control how a gesture stroke is sampled and recognized.
- * This class also has parameters to distinguish gesture input events from fast typing events.
- *
- * @attr ref android.R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
- * @attr ref android.R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
- * @attr ref android.R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
- * @attr ref android.R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
- * @attr ref android.R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
- * @attr ref android.R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
- */
-public final class GestureStrokeRecognitionParams {
- // Static threshold for gesture after fast typing
- public final int mStaticTimeThresholdAfterFastTyping; // msec
- // Static threshold for starting gesture detection
- public final float mDetectFastMoveSpeedThreshold; // keyWidth/sec
- // Dynamic threshold for gesture after fast typing
- public final int mDynamicThresholdDecayDuration; // msec
- // Time based threshold values
- public final int mDynamicTimeThresholdFrom; // msec
- public final int mDynamicTimeThresholdTo; // msec
- // Distance based threshold values
- public final float mDynamicDistanceThresholdFrom; // keyWidth
- public final float mDynamicDistanceThresholdTo; // keyWidth
- // Parameters for gesture sampling
- public final float mSamplingMinimumDistance; // keyWidth
- // Parameters for gesture recognition
- public final int mRecognitionMinimumTime; // msec
- public final float mRecognitionSpeedThreshold; // keyWidth/sec
-
- // Default GestureStrokeRecognitionPoints parameters.
- public static final GestureStrokeRecognitionParams DEFAULT =
- new GestureStrokeRecognitionParams();
-
- private GestureStrokeRecognitionParams() {
- // These parameter values are default and intended for testing.
- mStaticTimeThresholdAfterFastTyping = 350; // msec
- mDetectFastMoveSpeedThreshold = 1.5f; // keyWidth/sec
- mDynamicThresholdDecayDuration = 450; // msec
- mDynamicTimeThresholdFrom = 300; // msec
- mDynamicTimeThresholdTo = 20; // msec
- mDynamicDistanceThresholdFrom = 6.0f; // keyWidth
- mDynamicDistanceThresholdTo = 0.35f; // keyWidth
- // The following parameters' change will affect the result of regression test.
- mSamplingMinimumDistance = 1.0f / 6.0f; // keyWidth
- mRecognitionMinimumTime = 100; // msec
- mRecognitionSpeedThreshold = 5.5f; // keyWidth/sec
- }
-
- public GestureStrokeRecognitionParams(final TypedArray mainKeyboardViewAttr) {
- mStaticTimeThresholdAfterFastTyping = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping,
- DEFAULT.mStaticTimeThresholdAfterFastTyping);
- mDetectFastMoveSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
- R.styleable.MainKeyboardView_gestureDetectFastMoveSpeedThreshold,
- DEFAULT.mDetectFastMoveSpeedThreshold);
- mDynamicThresholdDecayDuration = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureDynamicThresholdDecayDuration,
- DEFAULT.mDynamicThresholdDecayDuration);
- mDynamicTimeThresholdFrom = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureDynamicTimeThresholdFrom,
- DEFAULT.mDynamicTimeThresholdFrom);
- mDynamicTimeThresholdTo = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureDynamicTimeThresholdTo,
- DEFAULT.mDynamicTimeThresholdTo);
- mDynamicDistanceThresholdFrom = ResourceUtils.getFraction(mainKeyboardViewAttr,
- R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdFrom,
- DEFAULT.mDynamicDistanceThresholdFrom);
- mDynamicDistanceThresholdTo = ResourceUtils.getFraction(mainKeyboardViewAttr,
- R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdTo,
- DEFAULT.mDynamicDistanceThresholdTo);
- mSamplingMinimumDistance = ResourceUtils.getFraction(mainKeyboardViewAttr,
- R.styleable.MainKeyboardView_gestureSamplingMinimumDistance,
- DEFAULT.mSamplingMinimumDistance);
- mRecognitionMinimumTime = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureRecognitionMinimumTime,
- DEFAULT.mRecognitionMinimumTime);
- mRecognitionSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
- R.styleable.MainKeyboardView_gestureRecognitionSpeedThreshold,
- DEFAULT.mRecognitionSpeedThreshold);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java
deleted file mode 100644
index 3e901114a..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.Log;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.common.ResizableIntArray;
-
-/**
- * This class holds event points to recognize a gesture stroke.
- * TODO: Should be package private class.
- */
-public final class GestureStrokeRecognitionPoints {
- private static final String TAG = GestureStrokeRecognitionPoints.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_SPEED = false;
-
- // The height of extra area above the keyboard to draw gesture trails.
- // Proportional to the keyboard height.
- public static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
-
- private final int mPointerId;
- private final ResizableIntArray mEventTimes = new ResizableIntArray(
- Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
- private final ResizableIntArray mXCoordinates = new ResizableIntArray(
- Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
- private final ResizableIntArray mYCoordinates = new ResizableIntArray(
- Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
-
- private final GestureStrokeRecognitionParams mRecognitionParams;
-
- private int mKeyWidth; // pixel
- private int mMinYCoordinate; // pixel
- private int mMaxYCoordinate; // pixel
- // Static threshold for starting gesture detection
- private int mDetectFastMoveSpeedThreshold; // pixel /sec
- private int mDetectFastMoveTime;
- private int mDetectFastMoveX;
- private int mDetectFastMoveY;
- // Dynamic threshold for gesture after fast typing
- private boolean mAfterFastTyping;
- private int mGestureDynamicDistanceThresholdFrom; // pixel
- private int mGestureDynamicDistanceThresholdTo; // pixel
- // Variables for gesture sampling
- private int mGestureSamplingMinimumDistance; // pixel
- private long mLastMajorEventTime;
- private int mLastMajorEventX;
- private int mLastMajorEventY;
- // Variables for gesture recognition
- private int mGestureRecognitionSpeedThreshold; // pixel / sec
- private int mIncrementalRecognitionSize;
- private int mLastIncrementalBatchSize;
-
- private static final int MSEC_PER_SEC = 1000;
-
- // TODO: Make this package private
- public GestureStrokeRecognitionPoints(final int pointerId,
- final GestureStrokeRecognitionParams recognitionParams) {
- mPointerId = pointerId;
- mRecognitionParams = recognitionParams;
- }
-
- // TODO: Make this package private
- public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
- mKeyWidth = keyWidth;
- mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
- mMaxYCoordinate = keyboardHeight;
- // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
- mDetectFastMoveSpeedThreshold = (int)(
- keyWidth * mRecognitionParams.mDetectFastMoveSpeedThreshold);
- mGestureDynamicDistanceThresholdFrom = (int)(
- keyWidth * mRecognitionParams.mDynamicDistanceThresholdFrom);
- mGestureDynamicDistanceThresholdTo = (int)(
- keyWidth * mRecognitionParams.mDynamicDistanceThresholdTo);
- mGestureSamplingMinimumDistance = (int)(
- keyWidth * mRecognitionParams.mSamplingMinimumDistance);
- mGestureRecognitionSpeedThreshold = (int)(
- keyWidth * mRecognitionParams.mRecognitionSpeedThreshold);
- if (DEBUG) {
- Log.d(TAG, String.format(
- "[%d] setKeyboardGeometry: keyWidth=%3d tT=%3d >> %3d tD=%3d >> %3d",
- mPointerId, keyWidth,
- mRecognitionParams.mDynamicTimeThresholdFrom,
- mRecognitionParams.mDynamicTimeThresholdTo,
- mGestureDynamicDistanceThresholdFrom,
- mGestureDynamicDistanceThresholdTo));
- }
- }
-
- // TODO: Make this package private
- public int getLength() {
- return mEventTimes.getLength();
- }
-
- // TODO: Make this package private
- public void addDownEventPoint(final int x, final int y, final int elapsedTimeSinceFirstDown,
- final int elapsedTimeSinceLastTyping) {
- reset();
- if (elapsedTimeSinceLastTyping < mRecognitionParams.mStaticTimeThresholdAfterFastTyping) {
- mAfterFastTyping = true;
- }
- if (DEBUG) {
- Log.d(TAG, String.format("[%d] onDownEvent: dT=%3d%s", mPointerId,
- elapsedTimeSinceLastTyping, mAfterFastTyping ? " afterFastTyping" : ""));
- }
- // Call {@link #addEventPoint(int,int,int,boolean)} to record this down event point as a
- // major event point.
- addEventPoint(x, y, elapsedTimeSinceFirstDown, true /* isMajorEvent */);
- }
-
- private int getGestureDynamicDistanceThreshold(final int deltaTime) {
- if (!mAfterFastTyping || deltaTime >= mRecognitionParams.mDynamicThresholdDecayDuration) {
- return mGestureDynamicDistanceThresholdTo;
- }
- final int decayedThreshold =
- (mGestureDynamicDistanceThresholdFrom - mGestureDynamicDistanceThresholdTo)
- * deltaTime / mRecognitionParams.mDynamicThresholdDecayDuration;
- return mGestureDynamicDistanceThresholdFrom - decayedThreshold;
- }
-
- private int getGestureDynamicTimeThreshold(final int deltaTime) {
- if (!mAfterFastTyping || deltaTime >= mRecognitionParams.mDynamicThresholdDecayDuration) {
- return mRecognitionParams.mDynamicTimeThresholdTo;
- }
- final int decayedThreshold =
- (mRecognitionParams.mDynamicTimeThresholdFrom
- - mRecognitionParams.mDynamicTimeThresholdTo)
- * deltaTime / mRecognitionParams.mDynamicThresholdDecayDuration;
- return mRecognitionParams.mDynamicTimeThresholdFrom - decayedThreshold;
- }
-
- // TODO: Make this package private
- public final boolean isStartOfAGesture() {
- if (!hasDetectedFastMove()) {
- return false;
- }
- final int size = getLength();
- if (size <= 0) {
- return false;
- }
- final int lastIndex = size - 1;
- final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
- if (deltaTime < 0) {
- return false;
- }
- final int deltaDistance = getDistance(
- mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
- mDetectFastMoveX, mDetectFastMoveY);
- final int distanceThreshold = getGestureDynamicDistanceThreshold(deltaTime);
- final int timeThreshold = getGestureDynamicTimeThreshold(deltaTime);
- final boolean isStartOfAGesture = deltaTime >= timeThreshold
- && deltaDistance >= distanceThreshold;
- if (DEBUG) {
- Log.d(TAG, String.format("[%d] isStartOfAGesture: dT=%3d tT=%3d dD=%3d tD=%3d%s%s",
- mPointerId, deltaTime, timeThreshold,
- deltaDistance, distanceThreshold,
- mAfterFastTyping ? " afterFastTyping" : "",
- isStartOfAGesture ? " startOfAGesture" : ""));
- }
- return isStartOfAGesture;
- }
-
- // TODO: Make this package private
- public void duplicateLastPointWith(final int time) {
- final int lastIndex = getLength() - 1;
- if (lastIndex >= 0) {
- final int x = mXCoordinates.get(lastIndex);
- final int y = mYCoordinates.get(lastIndex);
- if (DEBUG) {
- Log.d(TAG, String.format("[%d] duplicateLastPointWith: %d,%d|%d", mPointerId,
- x, y, time));
- }
- // TODO: Have appendMajorPoint()
- appendPoint(x, y, time);
- updateIncrementalRecognitionSize(x, y, time);
- }
- }
-
- private void reset() {
- mIncrementalRecognitionSize = 0;
- mLastIncrementalBatchSize = 0;
- mEventTimes.setLength(0);
- mXCoordinates.setLength(0);
- mYCoordinates.setLength(0);
- mLastMajorEventTime = 0;
- mDetectFastMoveTime = 0;
- mAfterFastTyping = false;
- }
-
- private void appendPoint(final int x, final int y, final int time) {
- final int lastIndex = getLength() - 1;
- // The point that is created by {@link duplicateLastPointWith(int)} may have later event
- // time than the next {@link MotionEvent}. To maintain the monotonicity of the event time,
- // drop the successive point here.
- if (lastIndex >= 0 && mEventTimes.get(lastIndex) > time) {
- Log.w(TAG, String.format("[%d] drop stale event: %d,%d|%d last: %d,%d|%d", mPointerId,
- x, y, time, mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
- mEventTimes.get(lastIndex)));
- return;
- }
- mEventTimes.add(time);
- mXCoordinates.add(x);
- mYCoordinates.add(y);
- }
-
- private void updateMajorEvent(final int x, final int y, final int time) {
- mLastMajorEventTime = time;
- mLastMajorEventX = x;
- mLastMajorEventY = y;
- }
-
- private final boolean hasDetectedFastMove() {
- return mDetectFastMoveTime > 0;
- }
-
- private int detectFastMove(final int x, final int y, final int time) {
- final int size = getLength();
- final int lastIndex = size - 1;
- final int lastX = mXCoordinates.get(lastIndex);
- final int lastY = mYCoordinates.get(lastIndex);
- final int dist = getDistance(lastX, lastY, x, y);
- final int msecs = time - mEventTimes.get(lastIndex);
- if (msecs > 0) {
- final int pixels = getDistance(lastX, lastY, x, y);
- final int pixelsPerSec = pixels * MSEC_PER_SEC;
- if (DEBUG_SPEED) {
- final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
- Log.d(TAG, String.format("[%d] detectFastMove: speed=%5.2f", mPointerId, speed));
- }
- // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
- if (!hasDetectedFastMove() && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
- if (DEBUG) {
- final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
- Log.d(TAG, String.format(
- "[%d] detectFastMove: speed=%5.2f T=%3d points=%3d fastMove",
- mPointerId, speed, time, size));
- }
- mDetectFastMoveTime = time;
- mDetectFastMoveX = x;
- mDetectFastMoveY = y;
- }
- }
- return dist;
- }
-
- /**
- * Add an event point to this gesture stroke recognition points. Returns true if the event
- * point is on the valid gesture area.
- * @param x the x-coordinate of the event point
- * @param y the y-coordinate of the event point
- * @param time the elapsed time in millisecond from the first gesture down
- * @param isMajorEvent false if this is a historical move event
- * @return true if the event point is on the valid gesture area
- */
- // TODO: Make this package private
- public boolean addEventPoint(final int x, final int y, final int time,
- final boolean isMajorEvent) {
- final int size = getLength();
- if (size <= 0) {
- // The first event of this stroke (a.k.a. down event).
- appendPoint(x, y, time);
- updateMajorEvent(x, y, time);
- } else {
- final int distance = detectFastMove(x, y, time);
- if (distance > mGestureSamplingMinimumDistance) {
- appendPoint(x, y, time);
- }
- }
- if (isMajorEvent) {
- updateIncrementalRecognitionSize(x, y, time);
- updateMajorEvent(x, y, time);
- }
- return y >= mMinYCoordinate && y < mMaxYCoordinate;
- }
-
- private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
- final int msecs = (int)(time - mLastMajorEventTime);
- if (msecs <= 0) {
- return;
- }
- final int pixels = getDistance(mLastMajorEventX, mLastMajorEventY, x, y);
- final int pixelsPerSec = pixels * MSEC_PER_SEC;
- // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
- if (pixelsPerSec < mGestureRecognitionSpeedThreshold * msecs) {
- mIncrementalRecognitionSize = getLength();
- }
- }
-
- // TODO: Make this package private
- public final boolean hasRecognitionTimePast(
- final long currentTime, final long lastRecognitionTime) {
- return currentTime > lastRecognitionTime + mRecognitionParams.mRecognitionMinimumTime;
- }
-
- // TODO: Make this package private
- public final void appendAllBatchPoints(final InputPointers out) {
- appendBatchPoints(out, getLength());
- }
-
- // TODO: Make this package private
- public final void appendIncrementalBatchPoints(final InputPointers out) {
- appendBatchPoints(out, mIncrementalRecognitionSize);
- }
-
- private void appendBatchPoints(final InputPointers out, final int size) {
- final int length = size - mLastIncrementalBatchSize;
- if (length <= 0) {
- return;
- }
- out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates,
- mLastIncrementalBatchSize, length);
- mLastIncrementalBatchSize = size;
- }
-
- private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
- return (int)Math.hypot(x1 - x2, y1 - y2);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingParams.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingParams.java
deleted file mode 100644
index 074862a70..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingParams.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-
-import com.android.inputmethod.latin.R;
-
-/**
- * This class holds parameters to control how a gesture trail is drawn and animated on the screen.
- *
- * On the other hand, {@link GestureStrokeDrawingParams} class controls how each gesture stroke is
- * sampled and interpolated. This class controls how those gesture strokes are displayed as a
- * gesture trail and animated on the screen.
- *
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailFadeoutStartDelay
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailFadeoutDuration
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailUpdateInterval
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailColor
- * @attr ref android.R.styleable#MainKeyboardView_gestureTrailWidth
- */
-final class GestureTrailDrawingParams {
- private static final int FADEOUT_START_DELAY_FOR_DEBUG = 2000; // millisecond
- private static final int FADEOUT_DURATION_FOR_DEBUG = 200; // millisecond
-
- public final int mTrailColor;
- public final float mTrailStartWidth;
- public final float mTrailEndWidth;
- public final float mTrailBodyRatio;
- public boolean mTrailShadowEnabled;
- public final float mTrailShadowRatio;
- public final int mFadeoutStartDelay;
- public final int mFadeoutDuration;
- public final int mUpdateInterval;
-
- public final int mTrailLingerDuration;
-
- public GestureTrailDrawingParams(final TypedArray mainKeyboardViewAttr) {
- mTrailColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_gestureTrailColor, 0);
- mTrailStartWidth = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_gestureTrailStartWidth, 0.0f);
- mTrailEndWidth = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_gestureTrailEndWidth, 0.0f);
- final int PERCENTAGE_INT = 100;
- mTrailBodyRatio = (float)mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureTrailBodyRatio, PERCENTAGE_INT)
- / (float)PERCENTAGE_INT;
- final int trailShadowRatioInt = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureTrailShadowRatio, 0);
- mTrailShadowEnabled = (trailShadowRatioInt > 0);
- mTrailShadowRatio = (float)trailShadowRatioInt / (float)PERCENTAGE_INT;
- mFadeoutStartDelay = GestureTrailDrawingPoints.DEBUG_SHOW_POINTS
- ? FADEOUT_START_DELAY_FOR_DEBUG
- : mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureTrailFadeoutStartDelay, 0);
- mFadeoutDuration = GestureTrailDrawingPoints.DEBUG_SHOW_POINTS
- ? FADEOUT_DURATION_FOR_DEBUG
- : mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureTrailFadeoutDuration, 0);
- mTrailLingerDuration = mFadeoutStartDelay + mFadeoutDuration;
- mUpdateInterval = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureTrailUpdateInterval, 0);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java
deleted file mode 100644
index 4d998e443..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.os.SystemClock;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.ResizableIntArray;
-
-/**
- * This class holds drawing points to represent a gesture trail. The gesture trail may contain
- * multiple non-contiguous gesture strokes and will be animated asynchronously from gesture input.
- *
- * On the other hand, {@link GestureStrokeDrawingPoints} class holds drawing points of each gesture
- * stroke. This class holds drawing points of those gesture strokes to draw as a gesture trail.
- * Drawing points in this class will be asynchronously removed when fading out animation goes.
- */
-final class GestureTrailDrawingPoints {
- public static final boolean DEBUG_SHOW_POINTS = false;
- public static final int POINT_TYPE_SAMPLED = 1;
- public static final int POINT_TYPE_INTERPOLATED = 2;
-
- private static final int DEFAULT_CAPACITY = GestureStrokeDrawingPoints.PREVIEW_CAPACITY;
-
- // These three {@link ResizableIntArray}s should be synchronized by {@link #mEventTimes}.
- private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
- private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
- private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
- private final ResizableIntArray mPointTypes = new ResizableIntArray(
- DEBUG_SHOW_POINTS ? DEFAULT_CAPACITY : 0);
- private int mCurrentStrokeId = -1;
- // The wall time of the zero value in {@link #mEventTimes}
- private long mCurrentTimeBase;
- private int mTrailStartIndex;
- private int mLastInterpolatedDrawIndex;
-
- // Use this value as imaginary zero because x-coordinates may be zero.
- private static final int DOWN_EVENT_MARKER = -128;
-
- private static int markAsDownEvent(final int xCoord) {
- return DOWN_EVENT_MARKER - xCoord;
- }
-
- private static boolean isDownEventXCoord(final int xCoordOrMark) {
- return xCoordOrMark <= DOWN_EVENT_MARKER;
- }
-
- private static int getXCoordValue(final int xCoordOrMark) {
- return isDownEventXCoord(xCoordOrMark)
- ? DOWN_EVENT_MARKER - xCoordOrMark : xCoordOrMark;
- }
-
- public void addStroke(final GestureStrokeDrawingPoints stroke, final long downTime) {
- synchronized (mEventTimes) {
- addStrokeLocked(stroke, downTime);
- }
- }
-
- private void addStrokeLocked(final GestureStrokeDrawingPoints stroke, final long downTime) {
- final int trailSize = mEventTimes.getLength();
- stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates, mPointTypes);
- if (mEventTimes.getLength() == trailSize) {
- return;
- }
- final int[] eventTimes = mEventTimes.getPrimitiveArray();
- final int strokeId = stroke.getGestureStrokeId();
- // Because interpolation algorithm in {@link GestureStrokeDrawingPoints} can't determine
- // the interpolated points in the last segment of gesture stroke, it may need recalculation
- // of interpolation when new segments are added to the stroke.
- // {@link #mLastInterpolatedDrawIndex} holds the start index of the last segment. It may
- // be updated by the interpolation
- // {@link GestureStrokeDrawingPoints#interpolatePreviewStroke}
- // or by animation {@link #drawGestureTrail(Canvas,Paint,Rect,GestureTrailDrawingParams)}
- // below.
- final int lastInterpolatedIndex = (strokeId == mCurrentStrokeId)
- ? mLastInterpolatedDrawIndex : trailSize;
- mLastInterpolatedDrawIndex = stroke.interpolateStrokeAndReturnStartIndexOfLastSegment(
- lastInterpolatedIndex, mEventTimes, mXCoordinates, mYCoordinates, mPointTypes);
- if (strokeId != mCurrentStrokeId) {
- final int elapsedTime = (int)(downTime - mCurrentTimeBase);
- for (int i = mTrailStartIndex; i < trailSize; i++) {
- // Decay the previous strokes' event times.
- eventTimes[i] -= elapsedTime;
- }
- final int[] xCoords = mXCoordinates.getPrimitiveArray();
- final int downIndex = trailSize;
- xCoords[downIndex] = markAsDownEvent(xCoords[downIndex]);
- mCurrentTimeBase = downTime - eventTimes[downIndex];
- mCurrentStrokeId = strokeId;
- }
- }
-
- /**
- * Calculate the alpha of a gesture trail.
- * A gesture trail starts from fully opaque. After mFadeStartDelay has been passed, the alpha
- * of a trail reduces in proportion to the elapsed time. Then after mFadeDuration has been
- * passed, a trail becomes fully transparent.
- *
- * @param elapsedTime the elapsed time since a trail has been made.
- * @param params gesture trail display parameters
- * @return the width of a gesture trail
- */
- private static int getAlpha(final int elapsedTime, final GestureTrailDrawingParams params) {
- if (elapsedTime < params.mFadeoutStartDelay) {
- return Constants.Color.ALPHA_OPAQUE;
- }
- final int decreasingAlpha = Constants.Color.ALPHA_OPAQUE
- * (elapsedTime - params.mFadeoutStartDelay)
- / params.mFadeoutDuration;
- return Constants.Color.ALPHA_OPAQUE - decreasingAlpha;
- }
-
- /**
- * Calculate the width of a gesture trail.
- * A gesture trail starts from the width of mTrailStartWidth and reduces its width in proportion
- * to the elapsed time. After mTrailEndWidth has been passed, the width becomes mTraiLEndWidth.
- *
- * @param elapsedTime the elapsed time since a trail has been made.
- * @param params gesture trail display parameters
- * @return the width of a gesture trail
- */
- private static float getWidth(final int elapsedTime, final GestureTrailDrawingParams params) {
- final float deltaWidth = params.mTrailStartWidth - params.mTrailEndWidth;
- return params.mTrailStartWidth - (deltaWidth * elapsedTime) / params.mTrailLingerDuration;
- }
-
- private final RoundedLine mRoundedLine = new RoundedLine();
- private final Rect mRoundedLineBounds = new Rect();
-
- /**
- * Draw gesture trail
- * @param canvas The canvas to draw the gesture trail
- * @param paint The paint object to be used to draw the gesture trail
- * @param outBoundsRect the bounding box of this gesture trail drawing
- * @param params The drawing parameters of gesture trail
- * @return true if some gesture trails remain to be drawn
- */
- public boolean drawGestureTrail(final Canvas canvas, final Paint paint,
- final Rect outBoundsRect, final GestureTrailDrawingParams params) {
- synchronized (mEventTimes) {
- return drawGestureTrailLocked(canvas, paint, outBoundsRect, params);
- }
- }
-
- private boolean drawGestureTrailLocked(final Canvas canvas, final Paint paint,
- final Rect outBoundsRect, final GestureTrailDrawingParams params) {
- // Initialize bounds rectangle.
- outBoundsRect.setEmpty();
- final int trailSize = mEventTimes.getLength();
- if (trailSize == 0) {
- return false;
- }
-
- final int[] eventTimes = mEventTimes.getPrimitiveArray();
- final int[] xCoords = mXCoordinates.getPrimitiveArray();
- final int[] yCoords = mYCoordinates.getPrimitiveArray();
- final int[] pointTypes = mPointTypes.getPrimitiveArray();
- final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentTimeBase);
- int startIndex;
- for (startIndex = mTrailStartIndex; startIndex < trailSize; startIndex++) {
- final int elapsedTime = sinceDown - eventTimes[startIndex];
- // Skip too old trail points.
- if (elapsedTime < params.mTrailLingerDuration) {
- break;
- }
- }
- mTrailStartIndex = startIndex;
-
- if (startIndex < trailSize) {
- paint.setColor(params.mTrailColor);
- paint.setStyle(Paint.Style.FILL);
- final RoundedLine roundedLine = mRoundedLine;
- int p1x = getXCoordValue(xCoords[startIndex]);
- int p1y = yCoords[startIndex];
- final int lastTime = sinceDown - eventTimes[startIndex];
- float r1 = getWidth(lastTime, params) / 2.0f;
- for (int i = startIndex + 1; i < trailSize; i++) {
- final int elapsedTime = sinceDown - eventTimes[i];
- final int p2x = getXCoordValue(xCoords[i]);
- final int p2y = yCoords[i];
- final float r2 = getWidth(elapsedTime, params) / 2.0f;
- // Draw trail line only when the current point isn't a down point.
- if (!isDownEventXCoord(xCoords[i])) {
- final float body1 = r1 * params.mTrailBodyRatio;
- final float body2 = r2 * params.mTrailBodyRatio;
- final Path path = roundedLine.makePath(p1x, p1y, body1, p2x, p2y, body2);
- if (!path.isEmpty()) {
- roundedLine.getBounds(mRoundedLineBounds);
- if (params.mTrailShadowEnabled) {
- final float shadow2 = r2 * params.mTrailShadowRatio;
- paint.setShadowLayer(shadow2, 0.0f, 0.0f, params.mTrailColor);
- final int shadowInset = -(int)Math.ceil(shadow2);
- mRoundedLineBounds.inset(shadowInset, shadowInset);
- }
- // Take union for the bounds.
- outBoundsRect.union(mRoundedLineBounds);
- final int alpha = getAlpha(elapsedTime, params);
- paint.setAlpha(alpha);
- canvas.drawPath(path, paint);
- }
- }
- p1x = p2x;
- p1y = p2y;
- r1 = r2;
- }
- if (DEBUG_SHOW_POINTS) {
- debugDrawPoints(canvas, startIndex, trailSize, paint);
- }
- }
-
- final int newSize = trailSize - startIndex;
- if (newSize < startIndex) {
- mTrailStartIndex = 0;
- if (newSize > 0) {
- System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize);
- System.arraycopy(xCoords, startIndex, xCoords, 0, newSize);
- System.arraycopy(yCoords, startIndex, yCoords, 0, newSize);
- if (DEBUG_SHOW_POINTS) {
- System.arraycopy(pointTypes, startIndex, pointTypes, 0, newSize);
- }
- }
- mEventTimes.setLength(newSize);
- mXCoordinates.setLength(newSize);
- mYCoordinates.setLength(newSize);
- if (DEBUG_SHOW_POINTS) {
- mPointTypes.setLength(newSize);
- }
- // The start index of the last segment of the stroke
- // {@link mLastInterpolatedDrawIndex} should also be updated because all array
- // elements have just been shifted for compaction or been zeroed.
- mLastInterpolatedDrawIndex = Math.max(mLastInterpolatedDrawIndex - startIndex, 0);
- }
- return newSize > 0;
- }
-
- private void debugDrawPoints(final Canvas canvas, final int startIndex, final int endIndex,
- final Paint paint) {
- final int[] xCoords = mXCoordinates.getPrimitiveArray();
- final int[] yCoords = mYCoordinates.getPrimitiveArray();
- final int[] pointTypes = mPointTypes.getPrimitiveArray();
- // {@link Paint} that is zero width stroke and anti alias off draws exactly 1 pixel.
- paint.setAntiAlias(false);
- paint.setStrokeWidth(0);
- for (int i = startIndex; i < endIndex; i++) {
- final int pointType = pointTypes[i];
- if (pointType == POINT_TYPE_INTERPOLATED) {
- paint.setColor(Color.RED);
- } else if (pointType == POINT_TYPE_SAMPLED) {
- paint.setColor(0xFFA000FF);
- } else {
- paint.setColor(Color.GREEN);
- }
- canvas.drawPoint(getXCoordValue(xCoords[i]), yCoords[i], paint);
- }
- paint.setAntiAlias(true);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java
deleted file mode 100644
index f7bd7efe0..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.util.SparseArray;
-
-import com.android.inputmethod.keyboard.PointerTracker;
-
-/**
- * Draw preview graphics of multiple gesture trails during gesture input.
- */
-public final class GestureTrailsDrawingPreview extends AbstractDrawingPreview implements Runnable {
- private final SparseArray<GestureTrailDrawingPoints> mGestureTrails = new SparseArray<>();
- private final GestureTrailDrawingParams mDrawingParams;
- private final Paint mGesturePaint;
- private int mOffscreenWidth;
- private int mOffscreenHeight;
- private int mOffscreenOffsetY;
- private Bitmap mOffscreenBuffer;
- private final Canvas mOffscreenCanvas = new Canvas();
- private final Rect mOffscreenSrcRect = new Rect();
- private final Rect mDirtyRect = new Rect();
- private final Rect mGestureTrailBoundsRect = new Rect(); // per trail
-
- private final Handler mDrawingHandler = new Handler();
-
- public GestureTrailsDrawingPreview(final TypedArray mainKeyboardViewAttr) {
- mDrawingParams = new GestureTrailDrawingParams(mainKeyboardViewAttr);
- final Paint gesturePaint = new Paint();
- gesturePaint.setAntiAlias(true);
- gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
- mGesturePaint = gesturePaint;
- }
-
- @Override
- public void setKeyboardViewGeometry(final int[] originCoords, final int width,
- final int height) {
- super.setKeyboardViewGeometry(originCoords, width, height);
- mOffscreenOffsetY = (int)(height
- * GestureStrokeRecognitionPoints.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
- mOffscreenWidth = width;
- mOffscreenHeight = mOffscreenOffsetY + height;
- }
-
- @Override
- public void onDeallocateMemory() {
- freeOffscreenBuffer();
- }
-
- private void freeOffscreenBuffer() {
- mOffscreenCanvas.setBitmap(null);
- mOffscreenCanvas.setMatrix(null);
- if (mOffscreenBuffer != null) {
- mOffscreenBuffer.recycle();
- mOffscreenBuffer = null;
- }
- }
-
- private void mayAllocateOffscreenBuffer() {
- if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth
- && mOffscreenBuffer.getHeight() == mOffscreenHeight) {
- return;
- }
- freeOffscreenBuffer();
- mOffscreenBuffer = Bitmap.createBitmap(
- mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888);
- mOffscreenCanvas.setBitmap(mOffscreenBuffer);
- mOffscreenCanvas.translate(0, mOffscreenOffsetY);
- }
-
- private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
- final Rect dirtyRect) {
- // Clear previous dirty rectangle.
- if (!dirtyRect.isEmpty()) {
- paint.setColor(Color.TRANSPARENT);
- paint.setStyle(Paint.Style.FILL);
- offscreenCanvas.drawRect(dirtyRect, paint);
- }
- dirtyRect.setEmpty();
- boolean needsUpdatingGestureTrail = false;
- // Draw gesture trails to offscreen buffer.
- synchronized (mGestureTrails) {
- // Trails count == fingers count that have ever been active.
- final int trailsCount = mGestureTrails.size();
- for (int index = 0; index < trailsCount; index++) {
- final GestureTrailDrawingPoints trail = mGestureTrails.valueAt(index);
- needsUpdatingGestureTrail |= trail.drawGestureTrail(offscreenCanvas, paint,
- mGestureTrailBoundsRect, mDrawingParams);
- // {@link #mGestureTrailBoundsRect} has bounding box of the trail.
- dirtyRect.union(mGestureTrailBoundsRect);
- }
- }
- return needsUpdatingGestureTrail;
- }
-
- @Override
- public void run() {
- // Update preview.
- invalidateDrawingView();
- }
-
- /**
- * Draws the preview
- * @param canvas The canvas where the preview is drawn.
- */
- @Override
- public void drawPreview(final Canvas canvas) {
- if (!isPreviewEnabled()) {
- return;
- }
- mayAllocateOffscreenBuffer();
- // Draw gesture trails to offscreen buffer.
- final boolean needsUpdatingGestureTrail = drawGestureTrails(
- mOffscreenCanvas, mGesturePaint, mDirtyRect);
- if (needsUpdatingGestureTrail) {
- mDrawingHandler.removeCallbacks(this);
- mDrawingHandler.postDelayed(this, mDrawingParams.mUpdateInterval);
- }
- // Transfer offscreen buffer to screen.
- if (!mDirtyRect.isEmpty()) {
- mOffscreenSrcRect.set(mDirtyRect);
- mOffscreenSrcRect.offset(0, mOffscreenOffsetY);
- canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null);
- // Note: Defer clearing the dirty rectangle here because we will get cleared
- // rectangle on the canvas.
- }
- }
-
- /**
- * Set the position of the preview.
- * @param tracker The new location of the preview is based on the points in PointerTracker.
- */
- @Override
- public void setPreviewPosition(final PointerTracker tracker) {
- if (!isPreviewEnabled()) {
- return;
- }
- GestureTrailDrawingPoints trail;
- synchronized (mGestureTrails) {
- trail = mGestureTrails.get(tracker.mPointerId);
- if (trail == null) {
- trail = new GestureTrailDrawingPoints();
- mGestureTrails.put(tracker.mPointerId, trail);
- }
- }
- trail.addStroke(tracker.getGestureStrokeDrawingPoints(), tracker.getDownTime());
-
- // TODO: Should narrow the invalidate region.
- invalidateDrawingView();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java b/java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java
deleted file mode 100644
index b526a942a..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-/**
- * Interpolates XY-coordinates using Cubic Hermite Curve.
- */
-public final class HermiteInterpolator {
- private int[] mXCoords;
- private int[] mYCoords;
- private int mMinPos;
- private int mMaxPos;
-
- // Working variable to calculate interpolated value.
- /** The coordinates of the start point of the interval. */
- public int mP1X, mP1Y;
- /** The coordinates of the end point of the interval. */
- public int mP2X, mP2Y;
- /** The slope of the tangent at the start point. */
- public float mSlope1X, mSlope1Y;
- /** The slope of the tangent at the end point. */
- public float mSlope2X, mSlope2Y;
- /** The interpolated coordinates.
- * The return variables of {@link #interpolate(float)} to avoid instantiations.
- */
- public float mInterpolatedX, mInterpolatedY;
-
- public HermiteInterpolator() {
- // Nothing to do with here.
- }
-
- /**
- * Reset this interpolator to point XY-coordinates data.
- * @param xCoords the array of x-coordinates. Valid data are in left-open interval
- * <code>[minPos, maxPos)</code>.
- * @param yCoords the array of y-coordinates. Valid data are in left-open interval
- * <code>[minPos, maxPos)</code>.
- * @param minPos the minimum index of left-open interval of valid data.
- * @param maxPos the maximum index of left-open interval of valid data.
- */
- public void reset(final int[] xCoords, final int[] yCoords, final int minPos,
- final int maxPos) {
- mXCoords = xCoords;
- mYCoords = yCoords;
- mMinPos = minPos;
- mMaxPos = maxPos;
- }
-
- /**
- * Set interpolation interval.
- * <p>
- * The start and end coordinates of the interval will be set in {@link #mP1X}, {@link #mP1Y},
- * {@link #mP2X}, and {@link #mP2Y}. The slope of the tangents at start and end points will be
- * set in {@link #mSlope1X}, {@link #mSlope1Y}, {@link #mSlope2X}, and {@link #mSlope2Y}.
- *
- * @param p0 the index just before interpolation interval. If <code>p1</code> points the start
- * of valid points, <code>p0</code> must be less than <code>minPos</code> of
- * {@link #reset(int[],int[],int,int)}.
- * @param p1 the start index of interpolation interval.
- * @param p2 the end index of interpolation interval.
- * @param p3 the index just after interpolation interval. If <code>p2</code> points the end of
- * valid points, <code>p3</code> must be equal or greater than <code>maxPos</code> of
- * {@link #reset(int[],int[],int,int)}.
- */
- public void setInterval(final int p0, final int p1, final int p2, final int p3) {
- mP1X = mXCoords[p1];
- mP1Y = mYCoords[p1];
- mP2X = mXCoords[p2];
- mP2Y = mYCoords[p2];
- // A(ax,ay) is the vector p1->p2.
- final int ax = mP2X - mP1X;
- final int ay = mP2Y - mP1Y;
-
- // Calculate the slope of the tangent at p1.
- if (p0 >= mMinPos) {
- // p1 has previous valid point p0.
- // The slope of the tangent is half of the vector p0->p2.
- mSlope1X = (mP2X - mXCoords[p0]) / 2.0f;
- mSlope1Y = (mP2Y - mYCoords[p0]) / 2.0f;
- } else if (p3 < mMaxPos) {
- // p1 has no previous valid point, but p2 has next valid point p3.
- // B(bx,by) is the slope vector of the tangent at p2.
- final float bx = (mXCoords[p3] - mP1X) / 2.0f;
- final float by = (mYCoords[p3] - mP1Y) / 2.0f;
- final float crossProdAB = ax * by - ay * bx;
- final float dotProdAB = ax * bx + ay * by;
- final float normASquare = ax * ax + ay * ay;
- final float invHalfNormASquare = 1.0f / normASquare / 2.0f;
- // The slope of the tangent is the mirror image of vector B to vector A.
- mSlope1X = invHalfNormASquare * (dotProdAB * ax + crossProdAB * ay);
- mSlope1Y = invHalfNormASquare * (dotProdAB * ay - crossProdAB * ax);
- } else {
- // p1 and p2 have no previous valid point. (Interval has only point p1 and p2)
- mSlope1X = ax;
- mSlope1Y = ay;
- }
-
- // Calculate the slope of the tangent at p2.
- if (p3 < mMaxPos) {
- // p2 has next valid point p3.
- // The slope of the tangent is half of the vector p1->p3.
- mSlope2X = (mXCoords[p3] - mP1X) / 2.0f;
- mSlope2Y = (mYCoords[p3] - mP1Y) / 2.0f;
- } else if (p0 >= mMinPos) {
- // p2 has no next valid point, but p1 has previous valid point p0.
- // B(bx,by) is the slope vector of the tangent at p1.
- final float bx = (mP2X - mXCoords[p0]) / 2.0f;
- final float by = (mP2Y - mYCoords[p0]) / 2.0f;
- final float crossProdAB = ax * by - ay * bx;
- final float dotProdAB = ax * bx + ay * by;
- final float normASquare = ax * ax + ay * ay;
- final float invHalfNormASquare = 1.0f / normASquare / 2.0f;
- // The slope of the tangent is the mirror image of vector B to vector A.
- mSlope2X = invHalfNormASquare * (dotProdAB * ax + crossProdAB * ay);
- mSlope2Y = invHalfNormASquare * (dotProdAB * ay - crossProdAB * ax);
- } else {
- // p1 and p2 has no previous valid point. (Interval has only point p1 and p2)
- mSlope2X = ax;
- mSlope2Y = ay;
- }
- }
-
- /**
- * Calculate interpolation value at <code>t</code> in unit interval <code>[0,1]</code>.
- * <p>
- * On the unit interval [0,1], given a starting point p1 at t=0 and an ending point p2 at t=1
- * with the slope of the tangent m1 at p1 and m2 at p2, the polynomial of cubic Hermite curve
- * can be defined by
- * p(t) = (1+2t)(1-t)(1-t)*p1 + t(1-t)(1-t)*m1 + (3-2t)t^2*p2 + (t-1)t^2*m2
- * where t is an element of [0,1].
- * <p>
- * The interpolated XY-coordinates will be set in {@link #mInterpolatedX} and
- * {@link #mInterpolatedY}.
- *
- * @param t the interpolation parameter. The value must be in close interval <code>[0,1]</code>.
- */
- public void interpolate(final float t) {
- final float omt = 1.0f - t;
- final float tm2 = 2.0f * t;
- final float k1 = 1.0f + tm2;
- final float k2 = 3.0f - tm2;
- final float omt2 = omt * omt;
- final float t2 = t * t;
- mInterpolatedX = (k1 * mP1X + t * mSlope1X) * omt2 + (k2 * mP2X - omt * mSlope2X) * t2;
- mInterpolatedY = (k1 * mP1Y + t * mSlope1Y) * omt2 + (k2 * mP2Y - omt * mSlope2Y) * t2;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
deleted file mode 100644
index 3ef9ea1dc..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.graphics.Typeface;
-
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class KeyDrawParams {
- @Nonnull
- public Typeface mTypeface = Typeface.DEFAULT;
-
- public int mLetterSize;
- public int mLabelSize;
- public int mLargeLetterSize;
- public int mHintLetterSize;
- public int mShiftedLetterHintSize;
- public int mHintLabelSize;
- public int mPreviewTextSize;
-
- public int mTextColor;
- public int mTextInactivatedColor;
- public int mTextShadowColor;
- public int mFunctionalTextColor;
- public int mHintLetterColor;
- public int mHintLabelColor;
- public int mShiftedLetterHintInactivatedColor;
- public int mShiftedLetterHintActivatedColor;
- public int mPreviewTextColor;
-
- public float mHintLabelVerticalAdjustment;
- public float mLabelOffCenterRatio;
- public float mHintLabelOffCenterRatio;
-
- public int mAnimAlpha;
-
- public KeyDrawParams() {}
-
- private KeyDrawParams(@Nonnull final KeyDrawParams copyFrom) {
- mTypeface = copyFrom.mTypeface;
-
- mLetterSize = copyFrom.mLetterSize;
- mLabelSize = copyFrom.mLabelSize;
- mLargeLetterSize = copyFrom.mLargeLetterSize;
- mHintLetterSize = copyFrom.mHintLetterSize;
- mShiftedLetterHintSize = copyFrom.mShiftedLetterHintSize;
- mHintLabelSize = copyFrom.mHintLabelSize;
- mPreviewTextSize = copyFrom.mPreviewTextSize;
-
- mTextColor = copyFrom.mTextColor;
- mTextInactivatedColor = copyFrom.mTextInactivatedColor;
- mTextShadowColor = copyFrom.mTextShadowColor;
- mFunctionalTextColor = copyFrom.mFunctionalTextColor;
- mHintLetterColor = copyFrom.mHintLetterColor;
- mHintLabelColor = copyFrom.mHintLabelColor;
- mShiftedLetterHintInactivatedColor = copyFrom.mShiftedLetterHintInactivatedColor;
- mShiftedLetterHintActivatedColor = copyFrom.mShiftedLetterHintActivatedColor;
- mPreviewTextColor = copyFrom.mPreviewTextColor;
-
- mHintLabelVerticalAdjustment = copyFrom.mHintLabelVerticalAdjustment;
- mLabelOffCenterRatio = copyFrom.mLabelOffCenterRatio;
- mHintLabelOffCenterRatio = copyFrom.mHintLabelOffCenterRatio;
-
- mAnimAlpha = copyFrom.mAnimAlpha;
- }
-
- public void updateParams(final int keyHeight, @Nullable final KeyVisualAttributes attr) {
- if (attr == null) {
- return;
- }
-
- if (attr.mTypeface != null) {
- mTypeface = attr.mTypeface;
- }
-
- mLetterSize = selectTextSizeFromDimensionOrRatio(keyHeight,
- attr.mLetterSize, attr.mLetterRatio, mLetterSize);
- mLabelSize = selectTextSizeFromDimensionOrRatio(keyHeight,
- attr.mLabelSize, attr.mLabelRatio, mLabelSize);
- mLargeLetterSize = selectTextSize(keyHeight, attr.mLargeLetterRatio, mLargeLetterSize);
- mHintLetterSize = selectTextSize(keyHeight, attr.mHintLetterRatio, mHintLetterSize);
- mShiftedLetterHintSize = selectTextSize(keyHeight,
- attr.mShiftedLetterHintRatio, mShiftedLetterHintSize);
- mHintLabelSize = selectTextSize(keyHeight, attr.mHintLabelRatio, mHintLabelSize);
- mPreviewTextSize = selectTextSize(keyHeight, attr.mPreviewTextRatio, mPreviewTextSize);
-
- mTextColor = selectColor(attr.mTextColor, mTextColor);
- mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
- mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
- mFunctionalTextColor = selectColor(attr.mFunctionalTextColor, mFunctionalTextColor);
- mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
- mHintLabelColor = selectColor(attr.mHintLabelColor, mHintLabelColor);
- mShiftedLetterHintInactivatedColor = selectColor(
- attr.mShiftedLetterHintInactivatedColor, mShiftedLetterHintInactivatedColor);
- mShiftedLetterHintActivatedColor = selectColor(
- attr.mShiftedLetterHintActivatedColor, mShiftedLetterHintActivatedColor);
- mPreviewTextColor = selectColor(attr.mPreviewTextColor, mPreviewTextColor);
-
- mHintLabelVerticalAdjustment = selectFloatIfNonZero(
- attr.mHintLabelVerticalAdjustment, mHintLabelVerticalAdjustment);
- mLabelOffCenterRatio = selectFloatIfNonZero(
- attr.mLabelOffCenterRatio, mLabelOffCenterRatio);
- mHintLabelOffCenterRatio = selectFloatIfNonZero(
- attr.mHintLabelOffCenterRatio, mHintLabelOffCenterRatio);
- }
-
- @Nonnull
- public KeyDrawParams mayCloneAndUpdateParams(final int keyHeight,
- @Nullable final KeyVisualAttributes attr) {
- if (attr == null) {
- return this;
- }
- final KeyDrawParams newParams = new KeyDrawParams(this);
- newParams.updateParams(keyHeight, attr);
- return newParams;
- }
-
- private static int selectTextSizeFromDimensionOrRatio(final int keyHeight,
- final int dimens, final float ratio, final int defaultDimens) {
- if (ResourceUtils.isValidDimensionPixelSize(dimens)) {
- return dimens;
- }
- if (ResourceUtils.isValidFraction(ratio)) {
- return (int)(keyHeight * ratio);
- }
- return defaultDimens;
- }
-
- private static int selectTextSize(final int keyHeight, final float ratio,
- final int defaultSize) {
- if (ResourceUtils.isValidFraction(ratio)) {
- return (int)(keyHeight * ratio);
- }
- return defaultSize;
- }
-
- private static int selectColor(final int attrColor, final int defaultColor) {
- if (attrColor != 0) {
- return attrColor;
- }
- return defaultColor;
- }
-
- private static float selectFloatIfNonZero(final float attrFloat, final float defaultFloat) {
- if (attrFloat != 0) {
- return attrFloat;
- }
- return defaultFloat;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
deleted file mode 100644
index 448f1b4b1..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-import com.android.inputmethod.latin.utils.ViewLayoutUtils;
-
-import java.util.ArrayDeque;
-import java.util.HashMap;
-
-/**
- * This class controls pop up key previews. This class decides:
- * - what kind of key previews should be shown.
- * - where key previews should be placed.
- * - how key previews should be shown and dismissed.
- */
-public final class KeyPreviewChoreographer {
- // Free {@link KeyPreviewView} pool that can be used for key preview.
- private final ArrayDeque<KeyPreviewView> mFreeKeyPreviewViews = new ArrayDeque<>();
- // Map from {@link Key} to {@link KeyPreviewView} that is currently being displayed as key
- // preview.
- private final HashMap<Key,KeyPreviewView> mShowingKeyPreviewViews = new HashMap<>();
-
- private final KeyPreviewDrawParams mParams;
-
- public KeyPreviewChoreographer(final KeyPreviewDrawParams params) {
- mParams = params;
- }
-
- public KeyPreviewView getKeyPreviewView(final Key key, final ViewGroup placerView) {
- KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.remove(key);
- if (keyPreviewView != null) {
- return keyPreviewView;
- }
- keyPreviewView = mFreeKeyPreviewViews.poll();
- if (keyPreviewView != null) {
- return keyPreviewView;
- }
- final Context context = placerView.getContext();
- keyPreviewView = new KeyPreviewView(context, null /* attrs */);
- keyPreviewView.setBackgroundResource(mParams.mPreviewBackgroundResId);
- placerView.addView(keyPreviewView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0));
- return keyPreviewView;
- }
-
- public boolean isShowingKeyPreview(final Key key) {
- return mShowingKeyPreviewViews.containsKey(key);
- }
-
- public void dismissKeyPreview(final Key key, final boolean withAnimation) {
- if (key == null) {
- return;
- }
- final KeyPreviewView keyPreviewView = mShowingKeyPreviewViews.get(key);
- if (keyPreviewView == null) {
- return;
- }
- final Object tag = keyPreviewView.getTag();
- if (withAnimation) {
- if (tag instanceof KeyPreviewAnimators) {
- final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag;
- animators.startDismiss();
- return;
- }
- }
- // Dismiss preview without animation.
- mShowingKeyPreviewViews.remove(key);
- if (tag instanceof Animator) {
- ((Animator)tag).cancel();
- }
- keyPreviewView.setTag(null);
- keyPreviewView.setVisibility(View.INVISIBLE);
- mFreeKeyPreviewViews.add(keyPreviewView);
- }
-
- public void placeAndShowKeyPreview(final Key key, final KeyboardIconsSet iconsSet,
- final KeyDrawParams drawParams, final int keyboardViewWidth, final int[] keyboardOrigin,
- final ViewGroup placerView, final boolean withAnimation) {
- final KeyPreviewView keyPreviewView = getKeyPreviewView(key, placerView);
- placeKeyPreview(
- key, keyPreviewView, iconsSet, drawParams, keyboardViewWidth, keyboardOrigin);
- showKeyPreview(key, keyPreviewView, withAnimation);
- }
-
- private void placeKeyPreview(final Key key, final KeyPreviewView keyPreviewView,
- final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams,
- final int keyboardViewWidth, final int[] originCoords) {
- keyPreviewView.setPreviewVisual(key, iconsSet, drawParams);
- keyPreviewView.measure(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- mParams.setGeometry(keyPreviewView);
- final int previewWidth = keyPreviewView.getMeasuredWidth();
- final int previewHeight = mParams.mPreviewHeight;
- final int keyDrawWidth = key.getDrawWidth();
- // The key preview is horizontally aligned with the center of the visible part of the
- // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
- // the left/right background is used if such background is specified.
- final int keyPreviewPosition;
- int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
- + CoordinateUtils.x(originCoords);
- if (previewX < 0) {
- previewX = 0;
- keyPreviewPosition = KeyPreviewView.POSITION_LEFT;
- } else if (previewX > keyboardViewWidth - previewWidth) {
- previewX = keyboardViewWidth - previewWidth;
- keyPreviewPosition = KeyPreviewView.POSITION_RIGHT;
- } else {
- keyPreviewPosition = KeyPreviewView.POSITION_MIDDLE;
- }
- final boolean hasMoreKeys = (key.getMoreKeys() != null);
- keyPreviewView.setPreviewBackground(hasMoreKeys, keyPreviewPosition);
- // The key preview is placed vertically above the top edge of the parent key with an
- // arbitrary offset.
- final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset
- + CoordinateUtils.y(originCoords);
-
- ViewLayoutUtils.placeViewAt(
- keyPreviewView, previewX, previewY, previewWidth, previewHeight);
- keyPreviewView.setPivotX(previewWidth / 2.0f);
- keyPreviewView.setPivotY(previewHeight);
- }
-
- void showKeyPreview(final Key key, final KeyPreviewView keyPreviewView,
- final boolean withAnimation) {
- if (!withAnimation) {
- keyPreviewView.setVisibility(View.VISIBLE);
- mShowingKeyPreviewViews.put(key, keyPreviewView);
- return;
- }
-
- // Show preview with animation.
- final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView);
- final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView);
- final KeyPreviewAnimators animators = new KeyPreviewAnimators(
- showUpAnimator, dismissAnimator);
- keyPreviewView.setTag(animators);
- animators.startShowUp();
- }
-
- public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) {
- final Animator showUpAnimator = mParams.createShowUpAnimator(keyPreviewView);
- showUpAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(final Animator animator) {
- showKeyPreview(key, keyPreviewView, false /* withAnimation */);
- }
- });
- return showUpAnimator;
- }
-
- private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) {
- final Animator dismissAnimator = mParams.createDismissAnimator(keyPreviewView);
- dismissAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animator) {
- dismissKeyPreview(key, false /* withAnimation */);
- }
- });
- return dismissAnimator;
- }
-
- private static class KeyPreviewAnimators extends AnimatorListenerAdapter {
- private final Animator mShowUpAnimator;
- private final Animator mDismissAnimator;
-
- public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) {
- mShowUpAnimator = showUpAnimator;
- mDismissAnimator = dismissAnimator;
- }
-
- public void startShowUp() {
- mShowUpAnimator.start();
- }
-
- public void startDismiss() {
- if (mShowUpAnimator.isRunning()) {
- mShowUpAnimator.addListener(this);
- return;
- }
- mDismissAnimator.start();
- }
-
- @Override
- public void onAnimationEnd(final Animator animator) {
- mDismissAnimator.start();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
deleted file mode 100644
index 5ed39f986..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.res.TypedArray;
-import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-
-import com.android.inputmethod.latin.R;
-
-public final class KeyPreviewDrawParams {
- // XML attributes of {@link MainKeyboardView}.
- public final int mPreviewOffset;
- public final int mPreviewHeight;
- public final int mPreviewBackgroundResId;
- private final int mShowUpAnimatorResId;
- private final int mDismissAnimatorResId;
- private boolean mHasCustomAnimationParams;
- private int mShowUpDuration;
- private int mDismissDuration;
- private float mShowUpStartXScale;
- private float mShowUpStartYScale;
- private float mDismissEndXScale;
- private float mDismissEndYScale;
- private int mLingerTimeout;
- private boolean mShowPopup = true;
-
- // The graphical geometry of the key preview.
- // <-width->
- // +-------+ ^
- // | | |
- // |preview| height (visible)
- // | | |
- // + + ^ v
- // \ / |offset
- // +-\ /-+ v
- // | +-+ |
- // |parent |
- // | key|
- // +-------+
- // The background of a {@link TextView} being used for a key preview may have invisible
- // paddings. To align the more keys keyboard panel's visible part with the visible part of
- // the background, we need to record the width and height of key preview that don't include
- // invisible paddings.
- private int mVisibleWidth;
- private int mVisibleHeight;
- // The key preview may have an arbitrary offset and its background that may have a bottom
- // padding. To align the more keys keyboard and the key preview we also need to record the
- // offset between the top edge of parent key and the bottom of the visible part of key
- // preview background.
- private int mVisibleOffset;
-
- public KeyPreviewDrawParams(final TypedArray mainKeyboardViewAttr) {
- mPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
- R.styleable.MainKeyboardView_keyPreviewOffset, 0);
- mPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
- R.styleable.MainKeyboardView_keyPreviewHeight, 0);
- mPreviewBackgroundResId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_keyPreviewBackground, 0);
- mLingerTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
- mShowUpAnimatorResId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_keyPreviewShowUpAnimator, 0);
- mDismissAnimatorResId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_keyPreviewDismissAnimator, 0);
- }
-
- public void setVisibleOffset(final int previewVisibleOffset) {
- mVisibleOffset = previewVisibleOffset;
- }
-
- public int getVisibleOffset() {
- return mVisibleOffset;
- }
-
- public void setGeometry(final View previewTextView) {
- final int previewWidth = previewTextView.getMeasuredWidth();
- final int previewHeight = mPreviewHeight;
- // The width and height of visible part of the key preview background. The content marker
- // of the background 9-patch have to cover the visible part of the background.
- mVisibleWidth = previewWidth - previewTextView.getPaddingLeft()
- - previewTextView.getPaddingRight();
- mVisibleHeight = previewHeight - previewTextView.getPaddingTop()
- - previewTextView.getPaddingBottom();
- // The distance between the top edge of the parent key and the bottom of the visible part
- // of the key preview background.
- setVisibleOffset(mPreviewOffset - previewTextView.getPaddingBottom());
- }
-
- public int getVisibleWidth() {
- return mVisibleWidth;
- }
-
- public int getVisibleHeight() {
- return mVisibleHeight;
- }
-
- public void setPopupEnabled(final boolean enabled, final int lingerTimeout) {
- mShowPopup = enabled;
- mLingerTimeout = lingerTimeout;
- }
-
- public boolean isPopupEnabled() {
- return mShowPopup;
- }
-
- public int getLingerTimeout() {
- return mLingerTimeout;
- }
-
- public void setAnimationParams(final boolean hasCustomAnimationParams,
- final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration,
- final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) {
- mHasCustomAnimationParams = hasCustomAnimationParams;
- mShowUpStartXScale = showUpStartXScale;
- mShowUpStartYScale = showUpStartYScale;
- mShowUpDuration = showUpDuration;
- mDismissEndXScale = dismissEndXScale;
- mDismissEndYScale = dismissEndYScale;
- mDismissDuration = dismissDuration;
- }
-
- private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f;
- private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
- new AccelerateInterpolator();
- private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
- new DecelerateInterpolator();
-
- public Animator createShowUpAnimator(final View target) {
- if (mHasCustomAnimationParams) {
- final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(
- target, View.SCALE_X, mShowUpStartXScale,
- KEY_PREVIEW_SHOW_UP_END_SCALE);
- final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(
- target, View.SCALE_Y, mShowUpStartYScale,
- KEY_PREVIEW_SHOW_UP_END_SCALE);
- final AnimatorSet showUpAnimator = new AnimatorSet();
- showUpAnimator.play(scaleXAnimator).with(scaleYAnimator);
- showUpAnimator.setDuration(mShowUpDuration);
- showUpAnimator.setInterpolator(DECELERATE_INTERPOLATOR);
- return showUpAnimator;
- }
- final Animator animator = AnimatorInflater.loadAnimator(
- target.getContext(), mShowUpAnimatorResId);
- animator.setTarget(target);
- animator.setInterpolator(DECELERATE_INTERPOLATOR);
- return animator;
- }
-
- public Animator createDismissAnimator(final View target) {
- if (mHasCustomAnimationParams) {
- final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(
- target, View.SCALE_X, mDismissEndXScale);
- final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(
- target, View.SCALE_Y, mDismissEndYScale);
- final AnimatorSet dismissAnimator = new AnimatorSet();
- dismissAnimator.play(scaleXAnimator).with(scaleYAnimator);
- final int dismissDuration = Math.min(mDismissDuration, mLingerTimeout);
- dismissAnimator.setDuration(dismissDuration);
- dismissAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
- return dismissAnimator;
- }
- final Animator animator = AnimatorInflater.loadAnimator(
- target.getContext(), mDismissAnimatorResId);
- animator.setTarget(target);
- animator.setInterpolator(ACCELERATE_INTERPOLATOR);
- return animator;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java
deleted file mode 100644
index 24538605a..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.widget.TextView;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.R;
-
-import java.util.HashSet;
-
-/**
- * The pop up key preview view.
- */
-public class KeyPreviewView extends TextView {
- public static final int POSITION_MIDDLE = 0;
- public static final int POSITION_LEFT = 1;
- public static final int POSITION_RIGHT = 2;
-
- private final Rect mBackgroundPadding = new Rect();
- private static final HashSet<String> sNoScaleXTextSet = new HashSet<>();
-
- public KeyPreviewView(final Context context, final AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyPreviewView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setGravity(Gravity.CENTER);
- }
-
- public void setPreviewVisual(final Key key, final KeyboardIconsSet iconsSet,
- final KeyDrawParams drawParams) {
- // What we show as preview should match what we show on a key top in onDraw().
- final int iconId = key.getIconId();
- if (iconId != KeyboardIconsSet.ICON_UNDEFINED) {
- setCompoundDrawables(null, null, null, key.getPreviewIcon(iconsSet));
- setText(null);
- return;
- }
-
- setCompoundDrawables(null, null, null, null);
- setTextColor(drawParams.mPreviewTextColor);
- setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams));
- setTypeface(key.selectPreviewTypeface(drawParams));
- // TODO Should take care of temporaryShiftLabel here.
- setTextAndScaleX(key.getPreviewLabel());
- }
-
- private void setTextAndScaleX(final String text) {
- setTextScaleX(1.0f);
- setText(text);
- if (sNoScaleXTextSet.contains(text)) {
- return;
- }
- // TODO: Override {@link #setBackground(Drawable)} that is supported from API 16 and
- // calculate maximum text width.
- final Drawable background = getBackground();
- if (background == null) {
- return;
- }
- background.getPadding(mBackgroundPadding);
- final int maxWidth = background.getIntrinsicWidth() - mBackgroundPadding.left
- - mBackgroundPadding.right;
- final float width = getTextWidth(text, getPaint());
- if (width <= maxWidth) {
- sNoScaleXTextSet.add(text);
- return;
- }
- setTextScaleX(maxWidth / width);
- }
-
- public static void clearTextCache() {
- sNoScaleXTextSet.clear();
- }
-
- private static float getTextWidth(final String text, final TextPaint paint) {
- if (TextUtils.isEmpty(text)) {
- return 0.0f;
- }
- final int len = text.length();
- final float[] widths = new float[len];
- final int count = paint.getTextWidths(text, 0, len, widths);
- float width = 0;
- for (int i = 0; i < count; i++) {
- width += widths[i];
- }
- return width;
- }
-
- // Background state set
- private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
- { // POSITION_MIDDLE
- {},
- { R.attr.state_has_morekeys }
- },
- { // POSITION_LEFT
- { R.attr.state_left_edge },
- { R.attr.state_left_edge, R.attr.state_has_morekeys }
- },
- { // POSITION_RIGHT
- { R.attr.state_right_edge },
- { R.attr.state_right_edge, R.attr.state_has_morekeys }
- }
- };
- private static final int STATE_NORMAL = 0;
- private static final int STATE_HAS_MOREKEYS = 1;
-
- public void setPreviewBackground(final boolean hasMoreKeys, final int position) {
- final Drawable background = getBackground();
- if (background == null) {
- return;
- }
- final int hasMoreKeysState = hasMoreKeys ? STATE_HAS_MOREKEYS : STATE_NORMAL;
- background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[position][hasMoreKeysState]);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
deleted file mode 100644
index 3eb62e7a6..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT;
-import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * The string parser of the key specification.
- *
- * Each key specification is one of the following:
- * - Label optionally followed by keyOutputText (keyLabel|keyOutputText).
- * - Label optionally followed by code point (keyLabel|!code/code_name).
- * - Icon followed by keyOutputText (!icon/icon_name|keyOutputText).
- * - Icon followed by code point (!icon/icon_name|!code/code_name).
- * Label and keyOutputText are one of the following:
- * - Literal string.
- * - Label reference represented by (!text/label_name), see {@link KeyboardTextsSet}.
- * - String resource reference represented by (!text/resource_name), see {@link KeyboardTextsSet}.
- * Icon is represented by (!icon/icon_name), see {@link KeyboardIconsSet}.
- * Code is one of the following:
- * - Code point presented by hexadecimal string prefixed with "0x"
- * - Code reference represented by (!code/code_name), see {@link KeyboardCodesSet}.
- * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\' character.
- * Note that the '\' is also parsed by XML parser and {@link MoreKeySpec#splitKeySpecs(String)}
- * as well.
- */
-// TODO: Rename to KeySpec and make this class to the key specification object.
-public final class KeySpecParser {
- // Constants for parsing.
- private static final char BACKSLASH = Constants.CODE_BACKSLASH;
- private static final char VERTICAL_BAR = Constants.CODE_VERTICAL_BAR;
- private static final String PREFIX_HEX = "0x";
-
- private KeySpecParser() {
- // Intentional empty constructor for utility class.
- }
-
- private static boolean hasIcon(@Nonnull final String keySpec) {
- return keySpec.startsWith(KeyboardIconsSet.PREFIX_ICON);
- }
-
- private static boolean hasCode(@Nonnull final String keySpec, final int labelEnd) {
- if (labelEnd <= 0 || labelEnd + 1 >= keySpec.length()) {
- return false;
- }
- if (keySpec.startsWith(KeyboardCodesSet.PREFIX_CODE, labelEnd + 1)) {
- return true;
- }
- // This is a workaround to have a key that has a supplementary code point. We can't put a
- // string in resource as a XML entity of a supplementary code point or a surrogate pair.
- if (keySpec.startsWith(PREFIX_HEX, labelEnd + 1)) {
- return true;
- }
- return false;
- }
-
- @Nonnull
- private static String parseEscape(@Nonnull final String text) {
- if (text.indexOf(BACKSLASH) < 0) {
- return text;
- }
- final int length = text.length();
- final StringBuilder sb = new StringBuilder();
- for (int pos = 0; pos < length; pos++) {
- final char c = text.charAt(pos);
- if (c == BACKSLASH && pos + 1 < length) {
- // Skip escape char
- pos++;
- sb.append(text.charAt(pos));
- } else {
- sb.append(c);
- }
- }
- return sb.toString();
- }
-
- private static int indexOfLabelEnd(@Nonnull final String keySpec) {
- final int length = keySpec.length();
- if (keySpec.indexOf(BACKSLASH) < 0) {
- final int labelEnd = keySpec.indexOf(VERTICAL_BAR);
- if (labelEnd == 0) {
- if (length == 1) {
- // Treat a sole vertical bar as a special case of key label.
- return -1;
- }
- throw new KeySpecParserError("Empty label");
- }
- return labelEnd;
- }
- for (int pos = 0; pos < length; pos++) {
- final char c = keySpec.charAt(pos);
- if (c == BACKSLASH && pos + 1 < length) {
- // Skip escape char
- pos++;
- } else if (c == VERTICAL_BAR) {
- return pos;
- }
- }
- return -1;
- }
-
- @Nonnull
- private static String getBeforeLabelEnd(@Nonnull final String keySpec, final int labelEnd) {
- return (labelEnd < 0) ? keySpec : keySpec.substring(0, labelEnd);
- }
-
- @Nonnull
- private static String getAfterLabelEnd(@Nonnull final String keySpec, final int labelEnd) {
- return keySpec.substring(labelEnd + /* VERTICAL_BAR */1);
- }
-
- private static void checkDoubleLabelEnd(@Nonnull final String keySpec, final int labelEnd) {
- if (indexOfLabelEnd(getAfterLabelEnd(keySpec, labelEnd)) < 0) {
- return;
- }
- throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + keySpec);
- }
-
- @Nullable
- public static String getLabel(@Nullable final String keySpec) {
- if (keySpec == null) {
- // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
- return null;
- }
- if (hasIcon(keySpec)) {
- return null;
- }
- final int labelEnd = indexOfLabelEnd(keySpec);
- final String label = parseEscape(getBeforeLabelEnd(keySpec, labelEnd));
- if (label.isEmpty()) {
- throw new KeySpecParserError("Empty label: " + keySpec);
- }
- return label;
- }
-
- @Nullable
- private static String getOutputTextInternal(@Nonnull final String keySpec, final int labelEnd) {
- if (labelEnd <= 0) {
- return null;
- }
- checkDoubleLabelEnd(keySpec, labelEnd);
- return parseEscape(getAfterLabelEnd(keySpec, labelEnd));
- }
-
- @Nullable
- public static String getOutputText(@Nullable final String keySpec) {
- if (keySpec == null) {
- // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
- return null;
- }
- final int labelEnd = indexOfLabelEnd(keySpec);
- if (hasCode(keySpec, labelEnd)) {
- return null;
- }
- final String outputText = getOutputTextInternal(keySpec, labelEnd);
- if (outputText != null) {
- if (StringUtils.codePointCount(outputText) == 1) {
- // If output text is one code point, it should be treated as a code.
- // See {@link #getCode(Resources, String)}.
- return null;
- }
- if (outputText.isEmpty()) {
- throw new KeySpecParserError("Empty outputText: " + keySpec);
- }
- return outputText;
- }
- final String label = getLabel(keySpec);
- if (label == null) {
- throw new KeySpecParserError("Empty label: " + keySpec);
- }
- // Code is automatically generated for one letter label. See {@link getCode()}.
- return (StringUtils.codePointCount(label) == 1) ? null : label;
- }
-
- public static int getCode(@Nullable final String keySpec) {
- if (keySpec == null) {
- // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
- return CODE_UNSPECIFIED;
- }
- final int labelEnd = indexOfLabelEnd(keySpec);
- if (hasCode(keySpec, labelEnd)) {
- checkDoubleLabelEnd(keySpec, labelEnd);
- return parseCode(getAfterLabelEnd(keySpec, labelEnd), CODE_UNSPECIFIED);
- }
- final String outputText = getOutputTextInternal(keySpec, labelEnd);
- if (outputText != null) {
- // If output text is one code point, it should be treated as a code.
- // See {@link #getOutputText(String)}.
- if (StringUtils.codePointCount(outputText) == 1) {
- return outputText.codePointAt(0);
- }
- return CODE_OUTPUT_TEXT;
- }
- final String label = getLabel(keySpec);
- if (label == null) {
- throw new KeySpecParserError("Empty label: " + keySpec);
- }
- // Code is automatically generated for one letter label.
- return (StringUtils.codePointCount(label) == 1) ? label.codePointAt(0) : CODE_OUTPUT_TEXT;
- }
-
- public static int parseCode(@Nullable final String text, final int defaultCode) {
- if (text == null) {
- return defaultCode;
- }
- if (text.startsWith(KeyboardCodesSet.PREFIX_CODE)) {
- return KeyboardCodesSet.getCode(text.substring(KeyboardCodesSet.PREFIX_CODE.length()));
- }
- // This is a workaround to have a key that has a supplementary code point. We can't put a
- // string in resource as a XML entity of a supplementary code point or a surrogate pair.
- if (text.startsWith(PREFIX_HEX)) {
- return Integer.parseInt(text.substring(PREFIX_HEX.length()), 16);
- }
- return defaultCode;
- }
-
- public static int getIconId(@Nullable final String keySpec) {
- if (keySpec == null) {
- // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
- return KeyboardIconsSet.ICON_UNDEFINED;
- }
- if (!hasIcon(keySpec)) {
- return KeyboardIconsSet.ICON_UNDEFINED;
- }
- final int labelEnd = indexOfLabelEnd(keySpec);
- final String iconName = getBeforeLabelEnd(keySpec, labelEnd)
- .substring(KeyboardIconsSet.PREFIX_ICON.length());
- return KeyboardIconsSet.getIconId(iconName);
- }
-
- @SuppressWarnings("serial")
- public static final class KeySpecParserError extends RuntimeException {
- public KeySpecParserError(final String message) {
- super(message);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
deleted file mode 100644
index 28aa22c16..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public abstract class KeyStyle {
- private final KeyboardTextsSet mTextsSet;
-
- public abstract @Nullable String[] getStringArray(TypedArray a, int index);
- public abstract @Nullable String getString(TypedArray a, int index);
- public abstract int getInt(TypedArray a, int index, int defaultValue);
- public abstract int getFlags(TypedArray a, int index);
-
- protected KeyStyle(@Nonnull final KeyboardTextsSet textsSet) {
- mTextsSet = textsSet;
- }
-
- @Nullable
- protected String parseString(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- return mTextsSet.resolveTextReference(a.getString(index));
- }
- return null;
- }
-
- @Nullable
- protected String[] parseStringArray(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- final String text = mTextsSet.resolveTextReference(a.getString(index));
- return MoreKeySpec.splitKeySpecs(text);
- }
- return null;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
deleted file mode 100644
index 61f98c8ff..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.XmlParseUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.util.Arrays;
-import java.util.HashMap;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class KeyStylesSet {
- private static final String TAG = KeyStylesSet.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- @Nonnull
- private final HashMap<String, KeyStyle> mStyles = new HashMap<>();
-
- @Nonnull
- private final KeyboardTextsSet mTextsSet;
- @Nonnull
- private final KeyStyle mEmptyKeyStyle;
- @Nonnull
- private static final String EMPTY_STYLE_NAME = "<empty>";
-
- public KeyStylesSet(@Nonnull final KeyboardTextsSet textsSet) {
- mTextsSet = textsSet;
- mEmptyKeyStyle = new EmptyKeyStyle(textsSet);
- mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
- }
-
- private static final class EmptyKeyStyle extends KeyStyle {
- EmptyKeyStyle(@Nonnull final KeyboardTextsSet textsSet) {
- super(textsSet);
- }
-
- @Override
- @Nullable
- public String[] getStringArray(final TypedArray a, final int index) {
- return parseStringArray(a, index);
- }
-
- @Override
- @Nullable
- public String getString(final TypedArray a, final int index) {
- return parseString(a, index);
- }
-
- @Override
- public int getInt(final TypedArray a, final int index, final int defaultValue) {
- return a.getInt(index, defaultValue);
- }
-
- @Override
- public int getFlags(final TypedArray a, final int index) {
- return a.getInt(index, 0);
- }
- }
-
- private static final class DeclaredKeyStyle extends KeyStyle {
- private final HashMap<String, KeyStyle> mStyles;
- private final String mParentStyleName;
- private final SparseArray<Object> mStyleAttributes = new SparseArray<>();
-
- public DeclaredKeyStyle(@Nonnull final String parentStyleName,
- @Nonnull final KeyboardTextsSet textsSet,
- @Nonnull final HashMap<String, KeyStyle> styles) {
- super(textsSet);
- mParentStyleName = parentStyleName;
- mStyles = styles;
- }
-
- @Override
- @Nullable
- public String[] getStringArray(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- return parseStringArray(a, index);
- }
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- final String[] array = (String[])value;
- return Arrays.copyOf(array, array.length);
- }
- final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return parentStyle.getStringArray(a, index);
- }
-
- @Override
- @Nullable
- public String getString(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- return parseString(a, index);
- }
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- return (String)value;
- }
- final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return parentStyle.getString(a, index);
- }
-
- @Override
- public int getInt(final TypedArray a, final int index, final int defaultValue) {
- if (a.hasValue(index)) {
- return a.getInt(index, defaultValue);
- }
- final Object value = mStyleAttributes.get(index);
- if (value != null) {
- return (Integer)value;
- }
- final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return parentStyle.getInt(a, index, defaultValue);
- }
-
- @Override
- public int getFlags(final TypedArray a, final int index) {
- final int parentFlags = mStyles.get(mParentStyleName).getFlags(a, index);
- final Integer value = (Integer)mStyleAttributes.get(index);
- final int styleFlags = (value != null) ? value : 0;
- final int flags = a.getInt(index, 0);
- return flags | styleFlags | parentFlags;
- }
-
- public void readKeyAttributes(final TypedArray keyAttr) {
- // TODO: Currently not all Key attributes can be declared as style.
- readString(keyAttr, R.styleable.Keyboard_Key_altCode);
- readString(keyAttr, R.styleable.Keyboard_Key_keySpec);
- readString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
- readStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
- readStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
- readFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
- readString(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled);
- readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn);
- readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType);
- readFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
- }
-
- private void readString(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- mStyleAttributes.put(index, parseString(a, index));
- }
- }
-
- private void readInt(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- mStyleAttributes.put(index, a.getInt(index, 0));
- }
- }
-
- private void readFlags(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- final Integer value = (Integer)mStyleAttributes.get(index);
- final int styleFlags = value != null ? value : 0;
- mStyleAttributes.put(index, a.getInt(index, 0) | styleFlags);
- }
- }
-
- private void readStringArray(final TypedArray a, final int index) {
- if (a.hasValue(index)) {
- mStyleAttributes.put(index, parseStringArray(a, index));
- }
- }
- }
-
- public void parseKeyStyleAttributes(final TypedArray keyStyleAttr, final TypedArray keyAttrs,
- final XmlPullParser parser) throws XmlPullParserException {
- final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
- if (styleName == null) {
- throw new XmlParseUtils.ParseException(
- KeyboardBuilder.TAG_KEY_STYLE + " has no styleName attribute", parser);
- }
- if (DEBUG) {
- Log.d(TAG, String.format("<%s styleName=%s />",
- KeyboardBuilder.TAG_KEY_STYLE, styleName));
- if (mStyles.containsKey(styleName)) {
- Log.d(TAG, KeyboardBuilder.TAG_KEY_STYLE + " " + styleName + " is overridden at "
- + parser.getPositionDescription());
- }
- }
-
- final String parentStyleInAttr = keyStyleAttr.getString(
- R.styleable.Keyboard_KeyStyle_parentStyle);
- if (parentStyleInAttr != null && !mStyles.containsKey(parentStyleInAttr)) {
- throw new XmlParseUtils.ParseException(
- "Unknown parentStyle " + parentStyleInAttr, parser);
- }
- final String parentStyleName = (parentStyleInAttr == null) ? EMPTY_STYLE_NAME
- : parentStyleInAttr;
- final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName, mTextsSet, mStyles);
- style.readKeyAttributes(keyAttrs);
- mStyles.put(styleName, style);
- }
-
- @Nonnull
- public KeyStyle getKeyStyle(final TypedArray keyAttr, final XmlPullParser parser)
- throws XmlParseUtils.ParseException {
- final String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle);
- if (styleName == null) {
- return mEmptyKeyStyle;
- }
- final KeyStyle style = mStyles.get(styleName);
- if (style == null) {
- throw new XmlParseUtils.ParseException("Unknown key style: " + styleName, parser);
- }
- return style;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
deleted file mode 100644
index 6f000d294..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-import android.graphics.Typeface;
-import android.util.SparseIntArray;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class KeyVisualAttributes {
- @Nullable
- public final Typeface mTypeface;
-
- public final float mLetterRatio;
- public final int mLetterSize;
- public final float mLabelRatio;
- public final int mLabelSize;
- public final float mLargeLetterRatio;
- public final float mHintLetterRatio;
- public final float mShiftedLetterHintRatio;
- public final float mHintLabelRatio;
- public final float mPreviewTextRatio;
-
- public final int mTextColor;
- public final int mTextInactivatedColor;
- public final int mTextShadowColor;
- public final int mFunctionalTextColor;
- public final int mHintLetterColor;
- public final int mHintLabelColor;
- public final int mShiftedLetterHintInactivatedColor;
- public final int mShiftedLetterHintActivatedColor;
- public final int mPreviewTextColor;
-
- public final float mHintLabelVerticalAdjustment;
- public final float mLabelOffCenterRatio;
- public final float mHintLabelOffCenterRatio;
-
- private static final int[] VISUAL_ATTRIBUTE_IDS = {
- R.styleable.Keyboard_Key_keyTypeface,
- R.styleable.Keyboard_Key_keyLetterSize,
- R.styleable.Keyboard_Key_keyLabelSize,
- R.styleable.Keyboard_Key_keyLargeLetterRatio,
- R.styleable.Keyboard_Key_keyHintLetterRatio,
- R.styleable.Keyboard_Key_keyShiftedLetterHintRatio,
- R.styleable.Keyboard_Key_keyHintLabelRatio,
- R.styleable.Keyboard_Key_keyPreviewTextRatio,
- R.styleable.Keyboard_Key_keyTextColor,
- R.styleable.Keyboard_Key_keyTextInactivatedColor,
- R.styleable.Keyboard_Key_keyTextShadowColor,
- R.styleable.Keyboard_Key_functionalTextColor,
- R.styleable.Keyboard_Key_keyHintLetterColor,
- R.styleable.Keyboard_Key_keyHintLabelColor,
- R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor,
- R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor,
- R.styleable.Keyboard_Key_keyPreviewTextColor,
- R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment,
- R.styleable.Keyboard_Key_keyLabelOffCenterRatio,
- R.styleable.Keyboard_Key_keyHintLabelOffCenterRatio
- };
- private static final SparseIntArray sVisualAttributeIds = new SparseIntArray();
- private static final int ATTR_DEFINED = 1;
- private static final int ATTR_NOT_FOUND = 0;
- static {
- for (final int attrId : VISUAL_ATTRIBUTE_IDS) {
- sVisualAttributeIds.put(attrId, ATTR_DEFINED);
- }
- }
-
- @Nullable
- public static KeyVisualAttributes newInstance(@Nonnull final TypedArray keyAttr) {
- final int indexCount = keyAttr.getIndexCount();
- for (int i = 0; i < indexCount; i++) {
- final int attrId = keyAttr.getIndex(i);
- if (sVisualAttributeIds.get(attrId, ATTR_NOT_FOUND) == ATTR_NOT_FOUND) {
- continue;
- }
- return new KeyVisualAttributes(keyAttr);
- }
- return null;
- }
-
- private KeyVisualAttributes(@Nonnull final TypedArray keyAttr) {
- if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyTypeface)) {
- mTypeface = Typeface.defaultFromStyle(
- keyAttr.getInt(R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL));
- } else {
- mTypeface = null;
- }
-
- mLetterRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyLetterSize);
- mLetterSize = ResourceUtils.getDimensionPixelSize(keyAttr,
- R.styleable.Keyboard_Key_keyLetterSize);
- mLabelRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyLabelSize);
- mLabelSize = ResourceUtils.getDimensionPixelSize(keyAttr,
- R.styleable.Keyboard_Key_keyLabelSize);
- mLargeLetterRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyLargeLetterRatio);
- mHintLetterRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyHintLetterRatio);
- mShiftedLetterHintRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyShiftedLetterHintRatio);
- mHintLabelRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyHintLabelRatio);
- mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyPreviewTextRatio);
-
- mTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextColor, 0);
- mTextInactivatedColor = keyAttr.getColor(
- R.styleable.Keyboard_Key_keyTextInactivatedColor, 0);
- mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0);
- mFunctionalTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_functionalTextColor, 0);
- mHintLetterColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLetterColor, 0);
- mHintLabelColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLabelColor, 0);
- mShiftedLetterHintInactivatedColor = keyAttr.getColor(
- R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, 0);
- mShiftedLetterHintActivatedColor = keyAttr.getColor(
- R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0);
- mPreviewTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyPreviewTextColor, 0);
-
- mHintLabelVerticalAdjustment = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment, 0.0f);
- mLabelOffCenterRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyLabelOffCenterRatio, 0.0f);
- mHintLabelOffCenterRatio = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyHintLabelOffCenterRatio, 0.0f);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
deleted file mode 100644
index 0eabf6cc9..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ /dev/null
@@ -1,889 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.util.Xml;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.KeyboardTheme;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.XmlParseUtils;
-import com.android.inputmethod.latin.utils.XmlParseUtils.ParseException;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-
-/**
- * Keyboard Building helper.
- *
- * This class parses Keyboard XML file and eventually build a Keyboard.
- * The Keyboard XML file looks like:
- * <pre>
- * &lt;!-- xml/keyboard.xml --&gt;
- * &lt;Keyboard keyboard_attributes*&gt;
- * &lt;!-- Keyboard Content --&gt;
- * &lt;Row row_attributes*&gt;
- * &lt;!-- Row Content --&gt;
- * &lt;Key key_attributes* /&gt;
- * &lt;Spacer horizontalGap="32.0dp" /&gt;
- * &lt;include keyboardLayout="@xml/other_keys"&gt;
- * ...
- * &lt;/Row&gt;
- * &lt;include keyboardLayout="@xml/other_rows"&gt;
- * ...
- * &lt;/Keyboard&gt;
- * </pre>
- * The XML file which is included in other file must have &lt;merge&gt; as root element,
- * such as:
- * <pre>
- * &lt;!-- xml/other_keys.xml --&gt;
- * &lt;merge&gt;
- * &lt;Key key_attributes* /&gt;
- * ...
- * &lt;/merge&gt;
- * </pre>
- * and
- * <pre>
- * &lt;!-- xml/other_rows.xml --&gt;
- * &lt;merge&gt;
- * &lt;Row row_attributes*&gt;
- * &lt;Key key_attributes* /&gt;
- * &lt;/Row&gt;
- * ...
- * &lt;/merge&gt;
- * </pre>
- * You can also use switch-case-default tags to select Rows and Keys.
- * <pre>
- * &lt;switch&gt;
- * &lt;case case_attribute*&gt;
- * &lt;!-- Any valid tags at switch position --&gt;
- * &lt;/case&gt;
- * ...
- * &lt;default&gt;
- * &lt;!-- Any valid tags at switch position --&gt;
- * &lt;/default&gt;
- * &lt;/switch&gt;
- * </pre>
- * You can declare Key style and specify styles within Key tags.
- * <pre>
- * &lt;switch&gt;
- * &lt;case mode="email"&gt;
- * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
- * keyLabel=".com"
- * /&gt;
- * &lt;/case&gt;
- * &lt;case mode="url"&gt;
- * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
- * keyLabel="http://"
- * /&gt;
- * &lt;/case&gt;
- * &lt;/switch&gt;
- * ...
- * &lt;Key keyStyle="shift-key" ... /&gt;
- * </pre>
- */
-
-// TODO: Write unit tests for this class.
-public class KeyboardBuilder<KP extends KeyboardParams> {
- private static final String BUILDER_TAG = "Keyboard.Builder";
- private static final boolean DEBUG = false;
-
- // Keyboard XML Tags
- private static final String TAG_KEYBOARD = "Keyboard";
- private static final String TAG_ROW = "Row";
- private static final String TAG_GRID_ROWS = "GridRows";
- private static final String TAG_KEY = "Key";
- private static final String TAG_SPACER = "Spacer";
- private static final String TAG_INCLUDE = "include";
- private static final String TAG_MERGE = "merge";
- private static final String TAG_SWITCH = "switch";
- private static final String TAG_CASE = "case";
- private static final String TAG_DEFAULT = "default";
- public static final String TAG_KEY_STYLE = "key-style";
-
- private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
- private static final int DEFAULT_KEYBOARD_ROWS = 4;
-
- @Nonnull
- protected final KP mParams;
- protected final Context mContext;
- protected final Resources mResources;
-
- private int mCurrentY = 0;
- private KeyboardRow mCurrentRow = null;
- private boolean mLeftEdge;
- private boolean mTopEdge;
- private Key mRightEdgeKey = null;
-
- public KeyboardBuilder(final Context context, @Nonnull final KP params) {
- mContext = context;
- final Resources res = context.getResources();
- mResources = res;
-
- mParams = params;
-
- params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
- params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
- }
-
- public void setAllowRedundantMoreKes(final boolean enabled) {
- mParams.mAllowRedundantMoreKeys = enabled;
- }
-
- public KeyboardBuilder<KP> load(final int xmlId, final KeyboardId id) {
- mParams.mId = id;
- final XmlResourceParser parser = mResources.getXml(xmlId);
- try {
- parseKeyboard(parser);
- } catch (XmlPullParserException e) {
- Log.w(BUILDER_TAG, "keyboard XML parse error", e);
- throw new IllegalArgumentException(e.getMessage(), e);
- } catch (IOException e) {
- Log.w(BUILDER_TAG, "keyboard XML parse error", e);
- throw new RuntimeException(e.getMessage(), e);
- } finally {
- parser.close();
- }
- return this;
- }
-
- @UsedForTesting
- public void disableTouchPositionCorrectionDataForTest() {
- mParams.mTouchPositionCorrection.setEnabled(false);
- }
-
- public void setProximityCharsCorrectionEnabled(final boolean enabled) {
- mParams.mProximityCharsCorrectionEnabled = enabled;
- }
-
- @Nonnull
- public Keyboard build() {
- return new Keyboard(mParams);
- }
-
- private int mIndent;
- private static final String SPACES = " ";
-
- private static String spaces(final int count) {
- return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
- }
-
- private void startTag(final String format, final Object ... args) {
- Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
- }
-
- private void endTag(final String format, final Object ... args) {
- Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
- }
-
- private void startEndTag(final String format, final Object ... args) {
- Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
- mIndent--;
- }
-
- private void parseKeyboard(final XmlPullParser parser)
- throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_KEYBOARD.equals(tag)) {
- parseKeyboardAttributes(parser);
- startKeyboard();
- parseKeyboardContent(parser, false);
- return;
- }
- throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD);
- }
- }
- }
-
- private void parseKeyboardAttributes(final XmlPullParser parser) {
- final AttributeSet attr = Xml.asAttributeSet(parser);
- final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
- attr, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard);
- final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
- try {
- final KeyboardParams params = mParams;
- final int height = params.mId.mHeight;
- final int width = params.mId.mWidth;
- params.mOccupiedHeight = height;
- params.mOccupiedWidth = width;
- params.mTopPadding = (int)keyboardAttr.getFraction(
- R.styleable.Keyboard_keyboardTopPadding, height, height, 0);
- params.mBottomPadding = (int)keyboardAttr.getFraction(
- R.styleable.Keyboard_keyboardBottomPadding, height, height, 0);
- params.mLeftPadding = (int)keyboardAttr.getFraction(
- R.styleable.Keyboard_keyboardLeftPadding, width, width, 0);
- params.mRightPadding = (int)keyboardAttr.getFraction(
- R.styleable.Keyboard_keyboardRightPadding, width, width, 0);
-
- final int baseWidth =
- params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding;
- params.mBaseWidth = baseWidth;
- params.mDefaultKeyWidth = (int)keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
- baseWidth, baseWidth, baseWidth / DEFAULT_KEYBOARD_COLUMNS);
- params.mHorizontalGap = (int)keyboardAttr.getFraction(
- R.styleable.Keyboard_horizontalGap, baseWidth, baseWidth, 0);
- // TODO: Fix keyboard geometry calculation clearer. Historically vertical gap between
- // rows are determined based on the entire keyboard height including top and bottom
- // paddings.
- params.mVerticalGap = (int)keyboardAttr.getFraction(
- R.styleable.Keyboard_verticalGap, height, height, 0);
- final int baseHeight = params.mOccupiedHeight - params.mTopPadding
- - params.mBottomPadding + params.mVerticalGap;
- params.mBaseHeight = baseHeight;
- params.mDefaultRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_rowHeight, baseHeight, baseHeight / DEFAULT_KEYBOARD_ROWS);
-
- params.mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
-
- params.mMoreKeysTemplate = keyboardAttr.getResourceId(
- R.styleable.Keyboard_moreKeysTemplate, 0);
- params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
- R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
-
- params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0);
- params.mIconsSet.loadIcons(keyboardAttr);
- params.mTextsSet.setLocale(params.mId.getLocale(), mContext);
-
- final int resourceId = keyboardAttr.getResourceId(
- R.styleable.Keyboard_touchPositionCorrectionData, 0);
- if (resourceId != 0) {
- final String[] data = mResources.getStringArray(resourceId);
- params.mTouchPositionCorrection.load(data);
- }
- } finally {
- keyAttr.recycle();
- keyboardAttr.recycle();
- }
- }
-
- private void parseKeyboardContent(final XmlPullParser parser, final boolean skip)
- throws XmlPullParserException, IOException {
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_ROW.equals(tag)) {
- final KeyboardRow row = parseRowAttributes(parser);
- if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
- if (!skip) {
- startRow(row);
- }
- parseRowContent(parser, row, skip);
- } else if (TAG_GRID_ROWS.equals(tag)) {
- if (DEBUG) startTag("<%s>%s", TAG_GRID_ROWS, skip ? " skipped" : "");
- parseGridRows(parser, skip);
- } else if (TAG_INCLUDE.equals(tag)) {
- parseIncludeKeyboardContent(parser, skip);
- } else if (TAG_SWITCH.equals(tag)) {
- parseSwitchKeyboardContent(parser, skip);
- } else if (TAG_KEY_STYLE.equals(tag)) {
- parseKeyStyle(parser, skip);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_ROW);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (DEBUG) endTag("</%s>", tag);
- if (TAG_KEYBOARD.equals(tag)) {
- endKeyboard();
- return;
- }
- if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) {
- return;
- }
- throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
- }
- }
- }
-
- private KeyboardRow parseRowAttributes(final XmlPullParser parser)
- throws XmlPullParserException {
- final AttributeSet attr = Xml.asAttributeSet(parser);
- final TypedArray keyboardAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard);
- try {
- if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap)) {
- throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "horizontalGap");
- }
- if (keyboardAttr.hasValue(R.styleable.Keyboard_verticalGap)) {
- throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "verticalGap");
- }
- return new KeyboardRow(mResources, mParams, parser, mCurrentY);
- } finally {
- keyboardAttr.recycle();
- }
- }
-
- private void parseRowContent(final XmlPullParser parser, final KeyboardRow row,
- final boolean skip) throws XmlPullParserException, IOException {
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_KEY.equals(tag)) {
- parseKey(parser, row, skip);
- } else if (TAG_SPACER.equals(tag)) {
- parseSpacer(parser, row, skip);
- } else if (TAG_INCLUDE.equals(tag)) {
- parseIncludeRowContent(parser, row, skip);
- } else if (TAG_SWITCH.equals(tag)) {
- parseSwitchRowContent(parser, row, skip);
- } else if (TAG_KEY_STYLE.equals(tag)) {
- parseKeyStyle(parser, skip);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_ROW);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (DEBUG) endTag("</%s>", tag);
- if (TAG_ROW.equals(tag)) {
- if (!skip) {
- endRow(row);
- }
- return;
- }
- if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) {
- return;
- }
- throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
- }
- }
- }
-
- private void parseGridRows(final XmlPullParser parser, final boolean skip)
- throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_GRID_ROWS, parser);
- if (DEBUG) {
- startEndTag("<%s /> skipped", TAG_GRID_ROWS);
- }
- return;
- }
- final KeyboardRow gridRows = new KeyboardRow(mResources, mParams, parser, mCurrentY);
- final TypedArray gridRowAttr = mResources.obtainAttributes(
- Xml.asAttributeSet(parser), R.styleable.Keyboard_GridRows);
- final int codesArrayId = gridRowAttr.getResourceId(
- R.styleable.Keyboard_GridRows_codesArray, 0);
- final int textsArrayId = gridRowAttr.getResourceId(
- R.styleable.Keyboard_GridRows_textsArray, 0);
- gridRowAttr.recycle();
- if (codesArrayId == 0 && textsArrayId == 0) {
- throw new XmlParseUtils.ParseException(
- "Missing codesArray or textsArray attributes", parser);
- }
- if (codesArrayId != 0 && textsArrayId != 0) {
- throw new XmlParseUtils.ParseException(
- "Both codesArray and textsArray attributes specifed", parser);
- }
- final String[] array = mResources.getStringArray(
- codesArrayId != 0 ? codesArrayId : textsArrayId);
- final int counts = array.length;
- final float keyWidth = gridRows.getKeyWidth(null, 0.0f);
- final int numColumns = (int)(mParams.mOccupiedWidth / keyWidth);
- for (int index = 0; index < counts; index += numColumns) {
- final KeyboardRow row = new KeyboardRow(mResources, mParams, parser, mCurrentY);
- startRow(row);
- for (int c = 0; c < numColumns; c++) {
- final int i = index + c;
- if (i >= counts) {
- break;
- }
- final String label;
- final int code;
- final String outputText;
- final int supportedMinSdkVersion;
- if (codesArrayId != 0) {
- final String codeArraySpec = array[i];
- label = CodesArrayParser.parseLabel(codeArraySpec);
- code = CodesArrayParser.parseCode(codeArraySpec);
- outputText = CodesArrayParser.parseOutputText(codeArraySpec);
- supportedMinSdkVersion =
- CodesArrayParser.getMinSupportSdkVersion(codeArraySpec);
- } else {
- final String textArraySpec = array[i];
- // TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
- label = textArraySpec;
- code = Constants.CODE_OUTPUT_TEXT;
- outputText = textArraySpec + (char)Constants.CODE_SPACE;
- supportedMinSdkVersion = 0;
- }
- if (Build.VERSION.SDK_INT < supportedMinSdkVersion) {
- continue;
- }
- final int labelFlags = row.getDefaultKeyLabelFlags();
- // TODO: Should be able to assign default keyActionFlags as well.
- final int backgroundType = row.getDefaultBackgroundType();
- final int x = (int)row.getKeyX(null);
- final int y = row.getKeyY();
- final int width = (int)keyWidth;
- final int height = row.getRowHeight();
- final Key key = new Key(label, KeyboardIconsSet.ICON_UNDEFINED, code, outputText,
- null /* hintLabel */, labelFlags, backgroundType, x, y, width, height,
- mParams.mHorizontalGap, mParams.mVerticalGap);
- endKey(key);
- row.advanceXPos(keyWidth);
- }
- endRow(row);
- }
-
- XmlParseUtils.checkEndTag(TAG_GRID_ROWS, parser);
- }
-
- private void parseKey(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
- throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_KEY, parser);
- if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
- return;
- }
- final TypedArray keyAttr = mResources.obtainAttributes(
- Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
- final KeyStyle keyStyle = mParams.mKeyStyles.getKeyStyle(keyAttr, parser);
- final String keySpec = keyStyle.getString(keyAttr, R.styleable.Keyboard_Key_keySpec);
- if (TextUtils.isEmpty(keySpec)) {
- throw new ParseException("Empty keySpec", parser);
- }
- final Key key = new Key(keySpec, keyAttr, keyStyle, mParams, row);
- keyAttr.recycle();
- if (DEBUG) {
- startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, (key.isEnabled() ? "" : " disabled"),
- key, Arrays.toString(key.getMoreKeys()));
- }
- XmlParseUtils.checkEndTag(TAG_KEY, parser);
- endKey(key);
- }
-
- private void parseSpacer(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
- throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_SPACER, parser);
- if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
- return;
- }
- final TypedArray keyAttr = mResources.obtainAttributes(
- Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
- final KeyStyle keyStyle = mParams.mKeyStyles.getKeyStyle(keyAttr, parser);
- final Key spacer = new Key.Spacer(keyAttr, keyStyle, mParams, row);
- keyAttr.recycle();
- if (DEBUG) startEndTag("<%s />", TAG_SPACER);
- XmlParseUtils.checkEndTag(TAG_SPACER, parser);
- endKey(spacer);
- }
-
- private void parseIncludeKeyboardContent(final XmlPullParser parser, final boolean skip)
- throws XmlPullParserException, IOException {
- parseIncludeInternal(parser, null, skip);
- }
-
- private void parseIncludeRowContent(final XmlPullParser parser, final KeyboardRow row,
- final boolean skip) throws XmlPullParserException, IOException {
- parseIncludeInternal(parser, row, skip);
- }
-
- private void parseIncludeInternal(final XmlPullParser parser, final KeyboardRow row,
- final boolean skip) throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
- if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
- return;
- }
- final AttributeSet attr = Xml.asAttributeSet(parser);
- final TypedArray keyboardAttr = mResources.obtainAttributes(
- attr, R.styleable.Keyboard_Include);
- final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
- int keyboardLayout = 0;
- try {
- XmlParseUtils.checkAttributeExists(
- keyboardAttr, R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
- TAG_INCLUDE, parser);
- keyboardLayout = keyboardAttr.getResourceId(
- R.styleable.Keyboard_Include_keyboardLayout, 0);
- if (row != null) {
- // Override current x coordinate.
- row.setXPos(row.getKeyX(keyAttr));
- // Push current Row attributes and update with new attributes.
- row.pushRowAttributes(keyAttr);
- }
- } finally {
- keyboardAttr.recycle();
- keyAttr.recycle();
- }
-
- XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
- if (DEBUG) {
- startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
- mResources.getResourceEntryName(keyboardLayout));
- }
- final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
- try {
- parseMerge(parserForInclude, row, skip);
- } finally {
- if (row != null) {
- // Restore Row attributes.
- row.popRowAttributes();
- }
- parserForInclude.close();
- }
- }
-
- private void parseMerge(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
- throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s>", TAG_MERGE);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_MERGE.equals(tag)) {
- if (row == null) {
- parseKeyboardContent(parser, skip);
- } else {
- parseRowContent(parser, row, skip);
- }
- return;
- }
- throw new XmlParseUtils.ParseException(
- "Included keyboard layout must have <merge> root element", parser);
- }
- }
- }
-
- private void parseSwitchKeyboardContent(final XmlPullParser parser, final boolean skip)
- throws XmlPullParserException, IOException {
- parseSwitchInternal(parser, null, skip);
- }
-
- private void parseSwitchRowContent(final XmlPullParser parser, final KeyboardRow row,
- final boolean skip) throws XmlPullParserException, IOException {
- parseSwitchInternal(parser, row, skip);
- }
-
- private void parseSwitchInternal(final XmlPullParser parser, final KeyboardRow row,
- final boolean skip) throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
- boolean selected = false;
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- final int event = parser.next();
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_CASE.equals(tag)) {
- selected |= parseCase(parser, row, selected ? true : skip);
- } else if (TAG_DEFAULT.equals(tag)) {
- selected |= parseDefault(parser, row, selected ? true : skip);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_SWITCH);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (TAG_SWITCH.equals(tag)) {
- if (DEBUG) endTag("</%s>", TAG_SWITCH);
- return;
- }
- throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_SWITCH);
- }
- }
- }
-
- private boolean parseCase(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
- throws XmlPullParserException, IOException {
- final boolean selected = parseCaseCondition(parser);
- if (row == null) {
- // Processing Rows.
- parseKeyboardContent(parser, selected ? skip : true);
- } else {
- // Processing Keys.
- parseRowContent(parser, row, selected ? skip : true);
- }
- return selected;
- }
-
- private boolean parseCaseCondition(final XmlPullParser parser) {
- final KeyboardId id = mParams.mId;
- if (id == null) {
- return true;
- }
- final AttributeSet attr = Xml.asAttributeSet(parser);
- final TypedArray caseAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Case);
- try {
- final boolean keyboardLayoutSetMatched = matchString(caseAttr,
- R.styleable.Keyboard_Case_keyboardLayoutSet,
- id.mSubtype.getKeyboardLayoutSetName());
- final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr,
- R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
- KeyboardId.elementIdToName(id.mElementId));
- final boolean keyboardThemeMacthed = matchTypedValue(caseAttr,
- R.styleable.Keyboard_Case_keyboardTheme, mParams.mThemeId,
- KeyboardTheme.getKeyboardThemeName(mParams.mThemeId));
- final boolean modeMatched = matchTypedValue(caseAttr,
- R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
- final boolean navigateNextMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
- final boolean navigatePreviousMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
- final boolean passwordInputMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
- final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
- final boolean hasShortcutKeyMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
- final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
- id.mLanguageSwitchKeyEnabled);
- final boolean isMultiLineMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
- final boolean imeActionMatched = matchInteger(caseAttr,
- R.styleable.Keyboard_Case_imeAction, id.imeAction());
- final boolean isIconDefinedMatched = isIconDefined(caseAttr,
- R.styleable.Keyboard_Case_isIconDefined, mParams.mIconsSet);
- final Locale locale = id.getLocale();
- final boolean localeCodeMatched = matchLocaleCodes(caseAttr, locale);
- final boolean languageCodeMatched = matchLanguageCodes(caseAttr, locale);
- final boolean countryCodeMatched = matchCountryCodes(caseAttr, locale);
- final boolean splitLayoutMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_isSplitLayout, id.mIsSplitLayout);
- final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched
- && keyboardThemeMacthed && modeMatched && navigateNextMatched
- && navigatePreviousMatched && passwordInputMatched && clobberSettingsKeyMatched
- && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
- && isMultiLineMatched && imeActionMatched && isIconDefinedMatched
- && localeCodeMatched && languageCodeMatched && countryCodeMatched
- && splitLayoutMatched;
-
- if (DEBUG) {
- startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
- textAttr(caseAttr.getString(
- R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"),
- textAttr(caseAttr.getString(
- R.styleable.Keyboard_Case_keyboardLayoutSetElement),
- "keyboardLayoutSetElement"),
- textAttr(caseAttr.getString(
- R.styleable.Keyboard_Case_keyboardTheme), "keyboardTheme"),
- textAttr(caseAttr.getString(R.styleable.Keyboard_Case_mode), "mode"),
- textAttr(caseAttr.getString(R.styleable.Keyboard_Case_imeAction),
- "imeAction"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_navigateNext,
- "navigateNext"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_navigatePrevious,
- "navigatePrevious"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_clobberSettingsKey,
- "clobberSettingsKey"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput,
- "passwordInput"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey,
- "hasShortcutKey"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
- "languageSwitchKeyEnabled"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_isMultiLine,
- "isMultiLine"),
- booleanAttr(caseAttr, R.styleable.Keyboard_Case_isSplitLayout,
- "splitLayout"),
- textAttr(caseAttr.getString(R.styleable.Keyboard_Case_isIconDefined),
- "isIconDefined"),
- textAttr(caseAttr.getString(R.styleable.Keyboard_Case_localeCode),
- "localeCode"),
- textAttr(caseAttr.getString(R.styleable.Keyboard_Case_languageCode),
- "languageCode"),
- textAttr(caseAttr.getString(R.styleable.Keyboard_Case_countryCode),
- "countryCode"),
- selected ? "" : " skipped");
- }
-
- return selected;
- } finally {
- caseAttr.recycle();
- }
- }
-
- private static boolean matchLocaleCodes(TypedArray caseAttr, final Locale locale) {
- return matchString(caseAttr, R.styleable.Keyboard_Case_localeCode, locale.toString());
- }
-
- private static boolean matchLanguageCodes(TypedArray caseAttr, Locale locale) {
- return matchString(caseAttr, R.styleable.Keyboard_Case_languageCode, locale.getLanguage());
- }
-
- private static boolean matchCountryCodes(TypedArray caseAttr, Locale locale) {
- return matchString(caseAttr, R.styleable.Keyboard_Case_countryCode, locale.getCountry());
- }
-
- private static boolean matchInteger(final TypedArray a, final int index, final int value) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- return !a.hasValue(index) || a.getInt(index, 0) == value;
- }
-
- private static boolean matchBoolean(final TypedArray a, final int index, final boolean value) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- return !a.hasValue(index) || a.getBoolean(index, false) == value;
- }
-
- private static boolean matchString(final TypedArray a, final int index, final String value) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- return !a.hasValue(index)
- || StringUtils.containsInArray(value, a.getString(index).split("\\|"));
- }
-
- private static boolean matchTypedValue(final TypedArray a, final int index, final int intValue,
- final String strValue) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- final TypedValue v = a.peekValue(index);
- if (v == null) {
- return true;
- }
- if (ResourceUtils.isIntegerValue(v)) {
- return intValue == a.getInt(index, 0);
- }
- if (ResourceUtils.isStringValue(v)) {
- return StringUtils.containsInArray(strValue, a.getString(index).split("\\|"));
- }
- return false;
- }
-
- private static boolean isIconDefined(final TypedArray a, final int index,
- final KeyboardIconsSet iconsSet) {
- if (!a.hasValue(index)) {
- return true;
- }
- final String iconName = a.getString(index);
- final int iconId = KeyboardIconsSet.getIconId(iconName);
- return iconsSet.getIconDrawable(iconId) != null;
- }
-
- private boolean parseDefault(final XmlPullParser parser, final KeyboardRow row,
- final boolean skip) throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s>", TAG_DEFAULT);
- if (row == null) {
- parseKeyboardContent(parser, skip);
- } else {
- parseRowContent(parser, row, skip);
- }
- return true;
- }
-
- private void parseKeyStyle(final XmlPullParser parser, final boolean skip)
- throws XmlPullParserException, IOException {
- final AttributeSet attr = Xml.asAttributeSet(parser);
- final TypedArray keyStyleAttr = mResources.obtainAttributes(
- attr, R.styleable.Keyboard_KeyStyle);
- final TypedArray keyAttrs = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
- try {
- if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
- throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
- + "/> needs styleName attribute", parser);
- }
- if (DEBUG) {
- startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
- keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
- skip ? " skipped" : "");
- }
- if (!skip) {
- mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
- }
- } finally {
- keyStyleAttr.recycle();
- keyAttrs.recycle();
- }
- XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
- }
-
- private void startKeyboard() {
- mCurrentY += mParams.mTopPadding;
- mTopEdge = true;
- }
-
- private void startRow(final KeyboardRow row) {
- addEdgeSpace(mParams.mLeftPadding, row);
- mCurrentRow = row;
- mLeftEdge = true;
- mRightEdgeKey = null;
- }
-
- private void endRow(final KeyboardRow row) {
- if (mCurrentRow == null) {
- throw new RuntimeException("orphan end row tag");
- }
- if (mRightEdgeKey != null) {
- mRightEdgeKey.markAsRightEdge(mParams);
- mRightEdgeKey = null;
- }
- addEdgeSpace(mParams.mRightPadding, row);
- mCurrentY += row.getRowHeight();
- mCurrentRow = null;
- mTopEdge = false;
- }
-
- private void endKey(@Nonnull final Key key) {
- mParams.onAddKey(key);
- if (mLeftEdge) {
- key.markAsLeftEdge(mParams);
- mLeftEdge = false;
- }
- if (mTopEdge) {
- key.markAsTopEdge(mParams);
- }
- mRightEdgeKey = key;
- }
-
- private void endKeyboard() {
- mParams.removeRedundantMoreKeys();
- // {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than
- // previously expected.
- final int actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding;
- mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight);
- }
-
- private void addEdgeSpace(final float width, final KeyboardRow row) {
- row.advanceXPos(width);
- mLeftEdge = false;
- mRightEdgeKey = null;
- }
-
- private static String textAttr(final String value, final String name) {
- return value != null ? String.format(" %s=%s", name, value) : "";
- }
-
- private static String booleanAttr(final TypedArray a, final int index, final String name) {
- return a.hasValue(index)
- ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
deleted file mode 100644
index 05b4c7473..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.latin.common.Constants;
-
-import java.util.HashMap;
-
-public final class KeyboardCodesSet {
- public static final String PREFIX_CODE = "!code/";
-
- private static final HashMap<String, Integer> sNameToIdMap = new HashMap<>();
-
- private KeyboardCodesSet() {
- // This utility class is not publicly instantiable.
- }
-
- public static int getCode(final String name) {
- Integer id = sNameToIdMap.get(name);
- if (id == null) throw new RuntimeException("Unknown key code: " + name);
- return DEFAULT[id];
- }
-
- private static final String[] ID_TO_NAME = {
- "key_tab",
- "key_enter",
- "key_space",
- "key_shift",
- "key_capslock",
- "key_switch_alpha_symbol",
- "key_output_text",
- "key_delete",
- "key_settings",
- "key_shortcut",
- "key_action_next",
- "key_action_previous",
- "key_shift_enter",
- "key_language_switch",
- "key_emoji",
- "key_alpha_from_emoji",
- "key_unspecified",
- };
-
- private static final int[] DEFAULT = {
- Constants.CODE_TAB,
- Constants.CODE_ENTER,
- Constants.CODE_SPACE,
- Constants.CODE_SHIFT,
- Constants.CODE_CAPSLOCK,
- Constants.CODE_SWITCH_ALPHA_SYMBOL,
- Constants.CODE_OUTPUT_TEXT,
- Constants.CODE_DELETE,
- Constants.CODE_SETTINGS,
- Constants.CODE_SHORTCUT,
- Constants.CODE_ACTION_NEXT,
- Constants.CODE_ACTION_PREVIOUS,
- Constants.CODE_SHIFT_ENTER,
- Constants.CODE_LANGUAGE_SWITCH,
- Constants.CODE_EMOJI,
- Constants.CODE_ALPHA_FROM_EMOJI,
- Constants.CODE_UNSPECIFIED,
- };
-
- static {
- for (int i = 0; i < ID_TO_NAME.length; i++) {
- sNameToIdMap.put(ID_TO_NAME[i], i);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
deleted file mode 100644
index 15a5bd456..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.util.SparseIntArray;
-
-import com.android.inputmethod.latin.R;
-
-import java.util.HashMap;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class KeyboardIconsSet {
- private static final String TAG = KeyboardIconsSet.class.getSimpleName();
-
- public static final String PREFIX_ICON = "!icon/";
- public static final int ICON_UNDEFINED = 0;
- private static final int ATTR_UNDEFINED = 0;
-
- private static final String NAME_UNDEFINED = "undefined";
- public static final String NAME_SHIFT_KEY = "shift_key";
- public static final String NAME_SHIFT_KEY_SHIFTED = "shift_key_shifted";
- public static final String NAME_DELETE_KEY = "delete_key";
- public static final String NAME_SETTINGS_KEY = "settings_key";
- public static final String NAME_SPACE_KEY = "space_key";
- public static final String NAME_SPACE_KEY_FOR_NUMBER_LAYOUT = "space_key_for_number_layout";
- public static final String NAME_ENTER_KEY = "enter_key";
- public static final String NAME_GO_KEY = "go_key";
- public static final String NAME_SEARCH_KEY = "search_key";
- public static final String NAME_SEND_KEY = "send_key";
- public static final String NAME_NEXT_KEY = "next_key";
- public static final String NAME_DONE_KEY = "done_key";
- public static final String NAME_PREVIOUS_KEY = "previous_key";
- public static final String NAME_TAB_KEY = "tab_key";
- public static final String NAME_SHORTCUT_KEY = "shortcut_key";
- public static final String NAME_SHORTCUT_KEY_DISABLED = "shortcut_key_disabled";
- public static final String NAME_LANGUAGE_SWITCH_KEY = "language_switch_key";
- public static final String NAME_ZWNJ_KEY = "zwnj_key";
- public static final String NAME_ZWJ_KEY = "zwj_key";
- public static final String NAME_EMOJI_ACTION_KEY = "emoji_action_key";
- public static final String NAME_EMOJI_NORMAL_KEY = "emoji_normal_key";
-
- private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray();
-
- // Icon name to icon id map.
- private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<>();
-
- private static final Object[] NAMES_AND_ATTR_IDS = {
- NAME_UNDEFINED, ATTR_UNDEFINED,
- NAME_SHIFT_KEY, R.styleable.Keyboard_iconShiftKey,
- NAME_DELETE_KEY, R.styleable.Keyboard_iconDeleteKey,
- NAME_SETTINGS_KEY, R.styleable.Keyboard_iconSettingsKey,
- NAME_SPACE_KEY, R.styleable.Keyboard_iconSpaceKey,
- NAME_ENTER_KEY, R.styleable.Keyboard_iconEnterKey,
- NAME_GO_KEY, R.styleable.Keyboard_iconGoKey,
- NAME_SEARCH_KEY, R.styleable.Keyboard_iconSearchKey,
- NAME_SEND_KEY, R.styleable.Keyboard_iconSendKey,
- NAME_NEXT_KEY, R.styleable.Keyboard_iconNextKey,
- NAME_DONE_KEY, R.styleable.Keyboard_iconDoneKey,
- NAME_PREVIOUS_KEY, R.styleable.Keyboard_iconPreviousKey,
- NAME_TAB_KEY, R.styleable.Keyboard_iconTabKey,
- NAME_SHORTCUT_KEY, R.styleable.Keyboard_iconShortcutKey,
- NAME_SPACE_KEY_FOR_NUMBER_LAYOUT, R.styleable.Keyboard_iconSpaceKeyForNumberLayout,
- NAME_SHIFT_KEY_SHIFTED, R.styleable.Keyboard_iconShiftKeyShifted,
- NAME_SHORTCUT_KEY_DISABLED, R.styleable.Keyboard_iconShortcutKeyDisabled,
- NAME_LANGUAGE_SWITCH_KEY, R.styleable.Keyboard_iconLanguageSwitchKey,
- NAME_ZWNJ_KEY, R.styleable.Keyboard_iconZwnjKey,
- NAME_ZWJ_KEY, R.styleable.Keyboard_iconZwjKey,
- NAME_EMOJI_ACTION_KEY, R.styleable.Keyboard_iconEmojiActionKey,
- NAME_EMOJI_NORMAL_KEY, R.styleable.Keyboard_iconEmojiNormalKey,
- };
-
- private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2;
- private static final String[] ICON_NAMES = new String[NUM_ICONS];
- private final Drawable[] mIcons = new Drawable[NUM_ICONS];
- private final int[] mIconResourceIds = new int[NUM_ICONS];
-
- static {
- int iconId = ICON_UNDEFINED;
- for (int i = 0; i < NAMES_AND_ATTR_IDS.length; i += 2) {
- final String name = (String)NAMES_AND_ATTR_IDS[i];
- final Integer attrId = (Integer)NAMES_AND_ATTR_IDS[i + 1];
- if (attrId != ATTR_UNDEFINED) {
- ATTR_ID_TO_ICON_ID.put(attrId, iconId);
- }
- sNameToIdsMap.put(name, iconId);
- ICON_NAMES[iconId] = name;
- iconId++;
- }
- }
-
- public void loadIcons(final TypedArray keyboardAttrs) {
- final int size = ATTR_ID_TO_ICON_ID.size();
- for (int index = 0; index < size; index++) {
- final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index);
- try {
- final Drawable icon = keyboardAttrs.getDrawable(attrId);
- setDefaultBounds(icon);
- final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId);
- mIcons[iconId] = icon;
- mIconResourceIds[iconId] = keyboardAttrs.getResourceId(attrId, 0);
- } catch (Resources.NotFoundException e) {
- Log.w(TAG, "Drawable resource for icon #"
- + keyboardAttrs.getResources().getResourceEntryName(attrId)
- + " not found");
- }
- }
- }
-
- private static boolean isValidIconId(final int iconId) {
- return iconId >= 0 && iconId < ICON_NAMES.length;
- }
-
- @Nonnull
- public static String getIconName(final int iconId) {
- return isValidIconId(iconId) ? ICON_NAMES[iconId] : "unknown<" + iconId + ">";
- }
-
- public static int getIconId(final String name) {
- Integer iconId = sNameToIdsMap.get(name);
- if (iconId != null) {
- return iconId;
- }
- throw new RuntimeException("unknown icon name: " + name);
- }
-
- public int getIconResourceId(final String name) {
- final int iconId = getIconId(name);
- if (isValidIconId(iconId)) {
- return mIconResourceIds[iconId];
- }
- throw new RuntimeException("unknown icon name: " + name);
- }
-
- @Nullable
- public Drawable getIconDrawable(final int iconId) {
- if (isValidIconId(iconId)) {
- return mIcons[iconId];
- }
- throw new RuntimeException("unknown icon id: " + getIconName(iconId));
- }
-
- private static void setDefaultBounds(final Drawable icon) {
- if (icon != null) {
- icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
deleted file mode 100644
index 738d6a400..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.SparseIntArray;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.latin.common.Constants;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class KeyboardParams {
- public KeyboardId mId;
- public int mThemeId;
-
- /** Total height and width of the keyboard, including the paddings and keys */
- public int mOccupiedHeight;
- public int mOccupiedWidth;
-
- /** Base height and width of the keyboard used to calculate rows' or keys' heights and
- * widths
- */
- public int mBaseHeight;
- public int mBaseWidth;
-
- public int mTopPadding;
- public int mBottomPadding;
- public int mLeftPadding;
- public int mRightPadding;
-
- @Nullable
- public KeyVisualAttributes mKeyVisualAttributes;
-
- public int mDefaultRowHeight;
- public int mDefaultKeyWidth;
- public int mHorizontalGap;
- public int mVerticalGap;
-
- public int mMoreKeysTemplate;
- public int mMaxMoreKeysKeyboardColumn;
-
- public int GRID_WIDTH;
- public int GRID_HEIGHT;
-
- // Keys are sorted from top-left to bottom-right order.
- @Nonnull
- public final SortedSet<Key> mSortedKeys = new TreeSet<>(ROW_COLUMN_COMPARATOR);
- @Nonnull
- public final ArrayList<Key> mShiftKeys = new ArrayList<>();
- @Nonnull
- public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<>();
- @Nonnull
- public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
- @Nonnull
- public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
- @Nonnull
- public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet);
-
- @Nonnull
- private final UniqueKeysCache mUniqueKeysCache;
- public boolean mAllowRedundantMoreKeys;
-
- public int mMostCommonKeyHeight = 0;
- public int mMostCommonKeyWidth = 0;
-
- public boolean mProximityCharsCorrectionEnabled;
-
- @Nonnull
- public final TouchPositionCorrection mTouchPositionCorrection =
- new TouchPositionCorrection();
-
- // Comparator to sort {@link Key}s from top-left to bottom-right order.
- private static final Comparator<Key> ROW_COLUMN_COMPARATOR = new Comparator<Key>() {
- @Override
- public int compare(final Key lhs, final Key rhs) {
- if (lhs.getY() < rhs.getY()) return -1;
- if (lhs.getY() > rhs.getY()) return 1;
- if (lhs.getX() < rhs.getX()) return -1;
- if (lhs.getX() > rhs.getX()) return 1;
- return 0;
- }
- };
-
- public KeyboardParams() {
- this(UniqueKeysCache.NO_CACHE);
- }
-
- public KeyboardParams(@Nonnull final UniqueKeysCache keysCache) {
- mUniqueKeysCache = keysCache;
- }
-
- protected void clearKeys() {
- mSortedKeys.clear();
- mShiftKeys.clear();
- clearHistogram();
- }
-
- public void onAddKey(@Nonnull final Key newKey) {
- final Key key = mUniqueKeysCache.getUniqueKey(newKey);
- final boolean isSpacer = key.isSpacer();
- if (isSpacer && key.getWidth() == 0) {
- // Ignore zero width {@link Spacer}.
- return;
- }
- mSortedKeys.add(key);
- if (isSpacer) {
- return;
- }
- updateHistogram(key);
- if (key.getCode() == Constants.CODE_SHIFT) {
- mShiftKeys.add(key);
- }
- if (key.altCodeWhileTyping()) {
- mAltCodeKeysWhileTyping.add(key);
- }
- }
-
- public void removeRedundantMoreKeys() {
- if (mAllowRedundantMoreKeys) {
- return;
- }
- final MoreKeySpec.LettersOnBaseLayout lettersOnBaseLayout =
- new MoreKeySpec.LettersOnBaseLayout();
- for (final Key key : mSortedKeys) {
- lettersOnBaseLayout.addLetter(key);
- }
- final ArrayList<Key> allKeys = new ArrayList<>(mSortedKeys);
- mSortedKeys.clear();
- for (final Key key : allKeys) {
- final Key filteredKey = Key.removeRedundantMoreKeys(key, lettersOnBaseLayout);
- mSortedKeys.add(mUniqueKeysCache.getUniqueKey(filteredKey));
- }
- }
-
- private int mMaxHeightCount = 0;
- private int mMaxWidthCount = 0;
- private final SparseIntArray mHeightHistogram = new SparseIntArray();
- private final SparseIntArray mWidthHistogram = new SparseIntArray();
-
- private void clearHistogram() {
- mMostCommonKeyHeight = 0;
- mMaxHeightCount = 0;
- mHeightHistogram.clear();
-
- mMaxWidthCount = 0;
- mMostCommonKeyWidth = 0;
- mWidthHistogram.clear();
- }
-
- private static int updateHistogramCounter(final SparseIntArray histogram, final int key) {
- final int index = histogram.indexOfKey(key);
- final int count = (index >= 0 ? histogram.get(key) : 0) + 1;
- histogram.put(key, count);
- return count;
- }
-
- private void updateHistogram(final Key key) {
- final int height = key.getHeight() + mVerticalGap;
- final int heightCount = updateHistogramCounter(mHeightHistogram, height);
- if (heightCount > mMaxHeightCount) {
- mMaxHeightCount = heightCount;
- mMostCommonKeyHeight = height;
- }
-
- final int width = key.getWidth() + mHorizontalGap;
- final int widthCount = updateHistogramCounter(mWidthHistogram, width);
- if (widthCount > mMaxWidthCount) {
- mMaxWidthCount = widthCount;
- mMostCommonKeyWidth = width;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
deleted file mode 100644
index 92daf0742..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.util.Xml;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.util.ArrayDeque;
-
-/**
- * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
- * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
- * defines.
- */
-public final class KeyboardRow {
- // keyWidth enum constants
- private static final int KEYWIDTH_NOT_ENUM = 0;
- private static final int KEYWIDTH_FILL_RIGHT = -1;
-
- private final KeyboardParams mParams;
- /** The height of this row. */
- private final int mRowHeight;
-
- private final ArrayDeque<RowAttributes> mRowAttributesStack = new ArrayDeque<>();
-
- // TODO: Add keyActionFlags.
- private static class RowAttributes {
- /** Default width of a key in this row. */
- public final float mDefaultKeyWidth;
- /** Default keyLabelFlags in this row. */
- public final int mDefaultKeyLabelFlags;
- /** Default backgroundType for this row */
- public final int mDefaultBackgroundType;
-
- /**
- * Parse and create key attributes. This constructor is used to parse Row tag.
- *
- * @param keyAttr an attributes array of Row tag.
- * @param defaultKeyWidth a default key width.
- * @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
- */
- public RowAttributes(final TypedArray keyAttr, final float defaultKeyWidth,
- final int keyboardWidth) {
- mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
- keyboardWidth, keyboardWidth, defaultKeyWidth);
- mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
- mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
- Key.BACKGROUND_TYPE_NORMAL);
- }
-
- /**
- * Parse and update key attributes using default attributes. This constructor is used
- * to parse include tag.
- *
- * @param keyAttr an attributes array of include tag.
- * @param defaultRowAttr default Row attributes.
- * @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
- */
- public RowAttributes(final TypedArray keyAttr, final RowAttributes defaultRowAttr,
- final int keyboardWidth) {
- mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
- keyboardWidth, keyboardWidth, defaultRowAttr.mDefaultKeyWidth);
- mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0)
- | defaultRowAttr.mDefaultKeyLabelFlags;
- mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
- defaultRowAttr.mDefaultBackgroundType);
- }
- }
-
- private final int mCurrentY;
- // Will be updated by {@link Key}'s constructor.
- private float mCurrentX;
-
- public KeyboardRow(final Resources res, final KeyboardParams params,
- final XmlPullParser parser, final int y) {
- mParams = params;
- final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard);
- mRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight);
- keyboardAttr.recycle();
- final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_Key);
- mRowAttributesStack.push(new RowAttributes(
- keyAttr, params.mDefaultKeyWidth, params.mBaseWidth));
- keyAttr.recycle();
-
- mCurrentY = y;
- mCurrentX = 0.0f;
- }
-
- public int getRowHeight() {
- return mRowHeight;
- }
-
- public void pushRowAttributes(final TypedArray keyAttr) {
- final RowAttributes newAttributes = new RowAttributes(
- keyAttr, mRowAttributesStack.peek(), mParams.mBaseWidth);
- mRowAttributesStack.push(newAttributes);
- }
-
- public void popRowAttributes() {
- mRowAttributesStack.pop();
- }
-
- public float getDefaultKeyWidth() {
- return mRowAttributesStack.peek().mDefaultKeyWidth;
- }
-
- public int getDefaultKeyLabelFlags() {
- return mRowAttributesStack.peek().mDefaultKeyLabelFlags;
- }
-
- public int getDefaultBackgroundType() {
- return mRowAttributesStack.peek().mDefaultBackgroundType;
- }
-
- public void setXPos(final float keyXPos) {
- mCurrentX = keyXPos;
- }
-
- public void advanceXPos(final float width) {
- mCurrentX += width;
- }
-
- public int getKeyY() {
- return mCurrentY;
- }
-
- public float getKeyX(final TypedArray keyAttr) {
- if (keyAttr == null || !keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
- return mCurrentX;
- }
- final float keyXPos = keyAttr.getFraction(R.styleable.Keyboard_Key_keyXPos,
- mParams.mBaseWidth, mParams.mBaseWidth, 0);
- if (keyXPos >= 0) {
- return keyXPos + mParams.mLeftPadding;
- }
- // If keyXPos is negative, the actual x-coordinate will be
- // keyboardWidth + keyXPos.
- // keyXPos shouldn't be less than mCurrentX because drawable area for this
- // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
- // its left hand side.
- final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
- return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
- }
-
- public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) {
- if (keyAttr == null) {
- return getDefaultKeyWidth();
- }
- final int widthType = ResourceUtils.getEnumValue(keyAttr,
- R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
- switch (widthType) {
- case KEYWIDTH_FILL_RIGHT:
- // If keyWidth is fillRight, the actual key width will be determined to fill
- // out the area up to the right edge of the keyboard.
- final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
- return keyboardRightEdge - keyXPos;
- default: // KEYWIDTH_NOT_ENUM
- return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
- mParams.mBaseWidth, mParams.mBaseWidth, getDefaultKeyWidth());
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
deleted file mode 100644
index 973e956db..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ /dev/null
@@ -1,711 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.event.Event;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.CapsModeUtils;
-import com.android.inputmethod.latin.utils.RecapitalizeStatus;
-
-/**
- * Keyboard state machine.
- *
- * This class contains all keyboard state transition logic.
- *
- * The input events are {@link #onLoadKeyboard(int, int)}, {@link #onSaveKeyboardState()},
- * {@link #onPressKey(int,boolean,int,int)}, {@link #onReleaseKey(int,boolean,int,int)},
- * {@link #onEvent(Event,int,int)}, {@link #onFinishSlidingInput(int,int)},
- * {@link #onUpdateShiftState(int,int)}, {@link #onResetKeyboardStateToAlphabet(int,int)}.
- *
- * The actions are {@link SwitchActions}'s methods.
- */
-public final class KeyboardState {
- private static final String TAG = KeyboardState.class.getSimpleName();
- private static final boolean DEBUG_EVENT = false;
- private static final boolean DEBUG_INTERNAL_ACTION = false;
-
- public interface SwitchActions {
- public static final boolean DEBUG_ACTION = false;
-
- public void setAlphabetKeyboard();
- public void setAlphabetManualShiftedKeyboard();
- public void setAlphabetAutomaticShiftedKeyboard();
- public void setAlphabetShiftLockedKeyboard();
- public void setAlphabetShiftLockShiftedKeyboard();
- public void setEmojiKeyboard();
- public void setSymbolsKeyboard();
- public void setSymbolsShiftedKeyboard();
-
- /**
- * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}.
- */
- public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode);
-
- public static final boolean DEBUG_TIMER_ACTION = false;
-
- public void startDoubleTapShiftKeyTimer();
- public boolean isInDoubleTapShiftKeyTimeout();
- public void cancelDoubleTapShiftKeyTimer();
- }
-
- private final SwitchActions mSwitchActions;
-
- private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
- private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
-
- // TODO: Merge {@link #mSwitchState}, {@link #mIsAlphabetMode}, {@link #mAlphabetShiftState},
- // {@link #mIsSymbolShifted}, {@link #mPrevMainKeyboardWasShiftLocked}, and
- // {@link #mPrevSymbolsKeyboardWasShifted} into single state variable.
- private static final int SWITCH_STATE_ALPHA = 0;
- private static final int SWITCH_STATE_SYMBOL_BEGIN = 1;
- private static final int SWITCH_STATE_SYMBOL = 2;
- private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3;
- private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4;
- private static final int SWITCH_STATE_MOMENTARY_ALPHA_SHIFT = 5;
- private int mSwitchState = SWITCH_STATE_ALPHA;
-
- // TODO: Consolidate these two mode booleans into one integer to distinguish between alphabet,
- // symbols, and emoji mode.
- private boolean mIsAlphabetMode;
- private boolean mIsEmojiMode;
- private AlphabetShiftState mAlphabetShiftState = new AlphabetShiftState();
- private boolean mIsSymbolShifted;
- private boolean mPrevMainKeyboardWasShiftLocked;
- private boolean mPrevSymbolsKeyboardWasShifted;
- private int mRecapitalizeMode;
-
- // For handling double tap.
- private boolean mIsInAlphabetUnshiftedFromShifted;
- private boolean mIsInDoubleTapShiftKey;
-
- private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState();
-
- static final class SavedKeyboardState {
- public boolean mIsValid;
- public boolean mIsAlphabetMode;
- public boolean mIsAlphabetShiftLocked;
- public boolean mIsEmojiMode;
- public int mShiftMode;
-
- @Override
- public String toString() {
- if (!mIsValid) {
- return "INVALID";
- }
- if (mIsAlphabetMode) {
- return mIsAlphabetShiftLocked ? "ALPHABET_SHIFT_LOCKED"
- : "ALPHABET_" + shiftModeToString(mShiftMode);
- }
- if (mIsEmojiMode) {
- return "EMOJI";
- }
- return "SYMBOLS_" + shiftModeToString(mShiftMode);
- }
- }
-
- public KeyboardState(final SwitchActions switchActions) {
- mSwitchActions = switchActions;
- mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
- }
-
- public void onLoadKeyboard(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_EVENT) {
- Log.d(TAG, "onLoadKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode));
- }
- // Reset alphabet shift state.
- mAlphabetShiftState.setShiftLocked(false);
- mPrevMainKeyboardWasShiftLocked = false;
- mPrevSymbolsKeyboardWasShifted = false;
- mShiftKeyState.onRelease();
- mSymbolKeyState.onRelease();
- if (mSavedKeyboardState.mIsValid) {
- onRestoreKeyboardState(autoCapsFlags, recapitalizeMode);
- mSavedKeyboardState.mIsValid = false;
- } else {
- // Reset keyboard to alphabet mode.
- setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
- }
- }
-
- // Constants for {@link SavedKeyboardState#mShiftMode} and {@link #setShifted(int)}.
- private static final int UNSHIFT = 0;
- private static final int MANUAL_SHIFT = 1;
- private static final int AUTOMATIC_SHIFT = 2;
- private static final int SHIFT_LOCK_SHIFTED = 3;
-
- public void onSaveKeyboardState() {
- final SavedKeyboardState state = mSavedKeyboardState;
- state.mIsAlphabetMode = mIsAlphabetMode;
- state.mIsEmojiMode = mIsEmojiMode;
- if (mIsAlphabetMode) {
- state.mIsAlphabetShiftLocked = mAlphabetShiftState.isShiftLocked();
- state.mShiftMode = mAlphabetShiftState.isAutomaticShifted() ? AUTOMATIC_SHIFT
- : (mAlphabetShiftState.isShiftedOrShiftLocked() ? MANUAL_SHIFT : UNSHIFT);
- } else {
- state.mIsAlphabetShiftLocked = mPrevMainKeyboardWasShiftLocked;
- state.mShiftMode = mIsSymbolShifted ? MANUAL_SHIFT : UNSHIFT;
- }
- state.mIsValid = true;
- if (DEBUG_EVENT) {
- Log.d(TAG, "onSaveKeyboardState: saved=" + state + " " + this);
- }
- }
-
- private void onRestoreKeyboardState(final int autoCapsFlags, final int recapitalizeMode) {
- final SavedKeyboardState state = mSavedKeyboardState;
- if (DEBUG_EVENT) {
- Log.d(TAG, "onRestoreKeyboardState: saved=" + state
- + " " + stateToString(autoCapsFlags, recapitalizeMode));
- }
- mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked;
- if (state.mIsAlphabetMode) {
- setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
- setShiftLocked(state.mIsAlphabetShiftLocked);
- if (!state.mIsAlphabetShiftLocked) {
- setShifted(state.mShiftMode);
- }
- return;
- }
- if (state.mIsEmojiMode) {
- setEmojiKeyboard();
- return;
- }
- // Symbol mode
- if (state.mShiftMode == MANUAL_SHIFT) {
- setSymbolsShiftedKeyboard();
- } else {
- setSymbolsKeyboard();
- }
- }
-
- private void setShifted(final int shiftMode) {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this);
- }
- if (!mIsAlphabetMode) return;
- final int prevShiftMode;
- if (mAlphabetShiftState.isAutomaticShifted()) {
- prevShiftMode = AUTOMATIC_SHIFT;
- } else if (mAlphabetShiftState.isManualShifted()) {
- prevShiftMode = MANUAL_SHIFT;
- } else {
- prevShiftMode = UNSHIFT;
- }
- switch (shiftMode) {
- case AUTOMATIC_SHIFT:
- mAlphabetShiftState.setAutomaticShifted();
- if (shiftMode != prevShiftMode) {
- mSwitchActions.setAlphabetAutomaticShiftedKeyboard();
- }
- break;
- case MANUAL_SHIFT:
- mAlphabetShiftState.setShifted(true);
- if (shiftMode != prevShiftMode) {
- mSwitchActions.setAlphabetManualShiftedKeyboard();
- }
- break;
- case UNSHIFT:
- mAlphabetShiftState.setShifted(false);
- if (shiftMode != prevShiftMode) {
- mSwitchActions.setAlphabetKeyboard();
- }
- break;
- case SHIFT_LOCK_SHIFTED:
- mAlphabetShiftState.setShifted(true);
- mSwitchActions.setAlphabetShiftLockShiftedKeyboard();
- break;
- }
- }
-
- private void setShiftLocked(final boolean shiftLocked) {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked + " " + this);
- }
- if (!mIsAlphabetMode) return;
- if (shiftLocked && (!mAlphabetShiftState.isShiftLocked()
- || mAlphabetShiftState.isShiftLockShifted())) {
- mSwitchActions.setAlphabetShiftLockedKeyboard();
- }
- if (!shiftLocked && mAlphabetShiftState.isShiftLocked()) {
- mSwitchActions.setAlphabetKeyboard();
- }
- mAlphabetShiftState.setShiftLocked(shiftLocked);
- }
-
- private void toggleAlphabetAndSymbols(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "toggleAlphabetAndSymbols: "
- + stateToString(autoCapsFlags, recapitalizeMode));
- }
- if (mIsAlphabetMode) {
- mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked();
- if (mPrevSymbolsKeyboardWasShifted) {
- setSymbolsShiftedKeyboard();
- } else {
- setSymbolsKeyboard();
- }
- mPrevSymbolsKeyboardWasShifted = false;
- } else {
- mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted;
- setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
- if (mPrevMainKeyboardWasShiftLocked) {
- setShiftLocked(true);
- }
- mPrevMainKeyboardWasShiftLocked = false;
- }
- }
-
- // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
- // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
- private void resetKeyboardStateToAlphabet(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "resetKeyboardStateToAlphabet: "
- + stateToString(autoCapsFlags, recapitalizeMode));
- }
- if (mIsAlphabetMode) return;
-
- mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted;
- setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
- if (mPrevMainKeyboardWasShiftLocked) {
- setShiftLocked(true);
- }
- mPrevMainKeyboardWasShiftLocked = false;
- }
-
- private void toggleShiftInSymbols() {
- if (mIsSymbolShifted) {
- setSymbolsKeyboard();
- } else {
- setSymbolsShiftedKeyboard();
- }
- }
-
- private void setAlphabetKeyboard(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "setAlphabetKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode));
- }
-
- mSwitchActions.setAlphabetKeyboard();
- mIsAlphabetMode = true;
- mIsEmojiMode = false;
- mIsSymbolShifted = false;
- mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
- mSwitchState = SWITCH_STATE_ALPHA;
- mSwitchActions.requestUpdatingShiftState(autoCapsFlags, recapitalizeMode);
- }
-
- private void setSymbolsKeyboard() {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "setSymbolsKeyboard");
- }
- mSwitchActions.setSymbolsKeyboard();
- mIsAlphabetMode = false;
- mIsSymbolShifted = false;
- mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
- // Reset alphabet shift state.
- mAlphabetShiftState.setShiftLocked(false);
- mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
- }
-
- private void setSymbolsShiftedKeyboard() {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "setSymbolsShiftedKeyboard");
- }
- mSwitchActions.setSymbolsShiftedKeyboard();
- mIsAlphabetMode = false;
- mIsSymbolShifted = true;
- mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
- // Reset alphabet shift state.
- mAlphabetShiftState.setShiftLocked(false);
- mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
- }
-
- private void setEmojiKeyboard() {
- if (DEBUG_INTERNAL_ACTION) {
- Log.d(TAG, "setEmojiKeyboard");
- }
- mIsAlphabetMode = false;
- mIsEmojiMode = true;
- mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
- // Remember caps lock mode and reset alphabet shift state.
- mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked();
- mAlphabetShiftState.setShiftLocked(false);
- mSwitchActions.setEmojiKeyboard();
- }
-
- public void onPressKey(final int code, final boolean isSinglePointer, final int autoCapsFlags,
- final int recapitalizeMode) {
- if (DEBUG_EVENT) {
- Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code)
- + " single=" + isSinglePointer
- + " " + stateToString(autoCapsFlags, recapitalizeMode));
- }
- if (code != Constants.CODE_SHIFT) {
- // Because the double tap shift key timer is to detect two consecutive shift key press,
- // it should be canceled when a non-shift key is pressed.
- mSwitchActions.cancelDoubleTapShiftKeyTimer();
- }
- if (code == Constants.CODE_SHIFT) {
- onPressShift();
- } else if (code == Constants.CODE_CAPSLOCK) {
- // Nothing to do here. See {@link #onReleaseKey(int,boolean)}.
- } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
- onPressSymbol(autoCapsFlags, recapitalizeMode);
- } else {
- mShiftKeyState.onOtherKeyPressed();
- mSymbolKeyState.onOtherKeyPressed();
- // It is required to reset the auto caps state when all of the following conditions
- // are met:
- // 1) two or more fingers are in action
- // 2) in alphabet layout
- // 3) not in all characters caps mode
- // As for #3, please note that it's required to check even when the auto caps mode is
- // off because, for example, we may be in the #1 state within the manual temporary
- // shifted mode.
- if (!isSinglePointer && mIsAlphabetMode
- && autoCapsFlags != TextUtils.CAP_MODE_CHARACTERS) {
- final boolean needsToResetAutoCaps = mAlphabetShiftState.isAutomaticShifted()
- || (mAlphabetShiftState.isManualShifted() && mShiftKeyState.isReleasing());
- if (needsToResetAutoCaps) {
- mSwitchActions.setAlphabetKeyboard();
- }
- }
- }
- }
-
- public void onReleaseKey(final int code, final boolean withSliding, final int autoCapsFlags,
- final int recapitalizeMode) {
- if (DEBUG_EVENT) {
- Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code)
- + " sliding=" + withSliding
- + " " + stateToString(autoCapsFlags, recapitalizeMode));
- }
- if (code == Constants.CODE_SHIFT) {
- onReleaseShift(withSliding, autoCapsFlags, recapitalizeMode);
- } else if (code == Constants.CODE_CAPSLOCK) {
- setShiftLocked(!mAlphabetShiftState.isShiftLocked());
- } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
- onReleaseSymbol(withSliding, autoCapsFlags, recapitalizeMode);
- }
- }
-
- private void onPressSymbol(final int autoCapsFlags,
- final int recapitalizeMode) {
- toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
- mSymbolKeyState.onPress();
- mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
- }
-
- private void onReleaseSymbol(final boolean withSliding, final int autoCapsFlags,
- final int recapitalizeMode) {
- if (mSymbolKeyState.isChording()) {
- // Switch back to the previous keyboard mode if the user chords the mode change key and
- // another key, then releases the mode change key.
- toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
- } else if (!withSliding) {
- // If the mode change key is being released without sliding, we should forget the
- // previous symbols keyboard shift state and simply switch back to symbols layout
- // (never symbols shifted) next time the mode gets changed to symbols layout.
- mPrevSymbolsKeyboardWasShifted = false;
- }
- mSymbolKeyState.onRelease();
- }
-
- public void onUpdateShiftState(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_EVENT) {
- Log.d(TAG, "onUpdateShiftState: " + stateToString(autoCapsFlags, recapitalizeMode));
- }
- mRecapitalizeMode = recapitalizeMode;
- updateAlphabetShiftState(autoCapsFlags, recapitalizeMode);
- }
-
- // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
- // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
- public void onResetKeyboardStateToAlphabet(final int autoCapsFlags,
- final int recapitalizeMode) {
- if (DEBUG_EVENT) {
- Log.d(TAG, "onResetKeyboardStateToAlphabet: "
- + stateToString(autoCapsFlags, recapitalizeMode));
- }
- resetKeyboardStateToAlphabet(autoCapsFlags, recapitalizeMode);
- }
-
- private void updateShiftStateForRecapitalize(final int recapitalizeMode) {
- switch (recapitalizeMode) {
- case RecapitalizeStatus.CAPS_MODE_ALL_UPPER:
- setShifted(SHIFT_LOCK_SHIFTED);
- break;
- case RecapitalizeStatus.CAPS_MODE_FIRST_WORD_UPPER:
- setShifted(AUTOMATIC_SHIFT);
- break;
- case RecapitalizeStatus.CAPS_MODE_ALL_LOWER:
- case RecapitalizeStatus.CAPS_MODE_ORIGINAL_MIXED_CASE:
- default:
- setShifted(UNSHIFT);
- }
- }
-
- private void updateAlphabetShiftState(final int autoCapsFlags, final int recapitalizeMode) {
- if (!mIsAlphabetMode) return;
- if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != recapitalizeMode) {
- // We are recapitalizing. Match the keyboard to the current recapitalize state.
- updateShiftStateForRecapitalize(recapitalizeMode);
- return;
- }
- if (!mShiftKeyState.isReleasing()) {
- // Ignore update shift state event while the shift key is being pressed (including
- // chording).
- return;
- }
- if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) {
- if (mShiftKeyState.isReleasing() && autoCapsFlags != Constants.TextUtils.CAP_MODE_OFF) {
- // Only when shift key is releasing, automatic temporary upper case will be set.
- setShifted(AUTOMATIC_SHIFT);
- } else {
- setShifted(mShiftKeyState.isChording() ? MANUAL_SHIFT : UNSHIFT);
- }
- }
- }
-
- private void onPressShift() {
- // If we are recapitalizing, we don't do any of the normal processing, including
- // importantly the double tap timer.
- if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) {
- return;
- }
- if (mIsAlphabetMode) {
- mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapShiftKeyTimeout();
- if (!mIsInDoubleTapShiftKey) {
- // This is first tap.
- mSwitchActions.startDoubleTapShiftKeyTimer();
- }
- if (mIsInDoubleTapShiftKey) {
- if (mAlphabetShiftState.isManualShifted() || mIsInAlphabetUnshiftedFromShifted) {
- // Shift key has been double tapped while in manual shifted or automatic
- // shifted state.
- setShiftLocked(true);
- } else {
- // Shift key has been double tapped while in normal state. This is the second
- // tap to disable shift locked state, so just ignore this.
- }
- } else {
- if (mAlphabetShiftState.isShiftLocked()) {
- // Shift key is pressed while shift locked state, we will treat this state as
- // shift lock shifted state and mark as if shift key pressed while normal
- // state.
- setShifted(SHIFT_LOCK_SHIFTED);
- mShiftKeyState.onPress();
- } else if (mAlphabetShiftState.isAutomaticShifted()) {
- // Shift key is pressed while automatic shifted, we have to move to manual
- // shifted.
- setShifted(MANUAL_SHIFT);
- mShiftKeyState.onPress();
- } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) {
- // In manual shifted state, we just record shift key has been pressing while
- // shifted state.
- mShiftKeyState.onPressOnShifted();
- } else {
- // In base layout, chording or manual shifted mode is started.
- setShifted(MANUAL_SHIFT);
- mShiftKeyState.onPress();
- }
- }
- } else {
- // In symbol mode, just toggle symbol and symbol more keyboard.
- toggleShiftInSymbols();
- mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
- mShiftKeyState.onPress();
- }
- }
-
- private void onReleaseShift(final boolean withSliding, final int autoCapsFlags,
- final int recapitalizeMode) {
- if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) {
- // We are recapitalizing. We should match the keyboard state to the recapitalize
- // state in priority.
- updateShiftStateForRecapitalize(mRecapitalizeMode);
- } else if (mIsAlphabetMode) {
- final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked();
- mIsInAlphabetUnshiftedFromShifted = false;
- if (mIsInDoubleTapShiftKey) {
- // Double tap shift key has been handled in {@link #onPressShift}, so that just
- // ignore this release shift key here.
- mIsInDoubleTapShiftKey = false;
- } else if (mShiftKeyState.isChording()) {
- if (mAlphabetShiftState.isShiftLockShifted()) {
- // After chording input while shift locked state.
- setShiftLocked(true);
- } else {
- // After chording input while normal state.
- setShifted(UNSHIFT);
- }
- // After chording input, automatic shift state may have been changed depending on
- // what characters were input.
- mShiftKeyState.onRelease();
- mSwitchActions.requestUpdatingShiftState(autoCapsFlags, recapitalizeMode);
- return;
- } else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) {
- // In shift locked state, shift has been pressed and slid out to other key.
- setShiftLocked(true);
- } else if (mAlphabetShiftState.isManualShifted() && withSliding) {
- // Shift has been pressed and slid out to other key.
- mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_SHIFT;
- } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted()
- && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted())
- && !withSliding) {
- // Shift has been long pressed, ignore this release.
- } else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) {
- // Shift has been pressed without chording while shift locked state.
- setShiftLocked(false);
- } else if (mAlphabetShiftState.isShiftedOrShiftLocked()
- && mShiftKeyState.isPressingOnShifted() && !withSliding) {
- // Shift has been pressed without chording while shifted state.
- setShifted(UNSHIFT);
- mIsInAlphabetUnshiftedFromShifted = true;
- } else if (mAlphabetShiftState.isManualShiftedFromAutomaticShifted()
- && mShiftKeyState.isPressing() && !withSliding) {
- // Shift has been pressed without chording while manual shifted transited from
- // automatic shifted
- setShifted(UNSHIFT);
- mIsInAlphabetUnshiftedFromShifted = true;
- }
- } else {
- // In symbol mode, switch back to the previous keyboard mode if the user chords the
- // shift key and another key, then releases the shift key.
- if (mShiftKeyState.isChording()) {
- toggleShiftInSymbols();
- }
- }
- mShiftKeyState.onRelease();
- }
-
- public void onFinishSlidingInput(final int autoCapsFlags, final int recapitalizeMode) {
- if (DEBUG_EVENT) {
- Log.d(TAG, "onFinishSlidingInput: " + stateToString(autoCapsFlags, recapitalizeMode));
- }
- // Switch back to the previous keyboard mode if the user cancels sliding input.
- switch (mSwitchState) {
- case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
- toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
- break;
- case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
- toggleShiftInSymbols();
- break;
- case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT:
- setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
- break;
- }
- }
-
- private static boolean isSpaceOrEnter(final int c) {
- return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER;
- }
-
- public void onEvent(final Event event, final int autoCapsFlags, final int recapitalizeMode) {
- final int code = event.isFunctionalKeyEvent() ? event.mKeyCode : event.mCodePoint;
- if (DEBUG_EVENT) {
- Log.d(TAG, "onEvent: code=" + Constants.printableCode(code)
- + " " + stateToString(autoCapsFlags, recapitalizeMode));
- }
-
- switch (mSwitchState) {
- case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
- if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
- // Detected only the mode change key has been pressed, and then released.
- if (mIsAlphabetMode) {
- mSwitchState = SWITCH_STATE_ALPHA;
- } else {
- mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
- }
- }
- break;
- case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
- if (code == Constants.CODE_SHIFT) {
- // Detected only the shift key has been pressed on symbol layout, and then
- // released.
- mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
- }
- break;
- case SWITCH_STATE_SYMBOL_BEGIN:
- if (mIsEmojiMode) {
- // When in the Emoji keyboard, we don't want to switch back to the main layout even
- // after the user hits an emoji letter followed by an enter or a space.
- break;
- }
- if (!isSpaceOrEnter(code) && (Constants.isLetterCode(code)
- || code == Constants.CODE_OUTPUT_TEXT)) {
- mSwitchState = SWITCH_STATE_SYMBOL;
- }
- break;
- case SWITCH_STATE_SYMBOL:
- // Switch back to alpha keyboard mode if user types one or more non-space/enter
- // characters followed by a space/enter.
- if (isSpaceOrEnter(code)) {
- toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
- mPrevSymbolsKeyboardWasShifted = false;
- }
- break;
- }
-
- // If the code is a letter, update keyboard shift state.
- if (Constants.isLetterCode(code)) {
- updateAlphabetShiftState(autoCapsFlags, recapitalizeMode);
- } else if (code == Constants.CODE_EMOJI) {
- setEmojiKeyboard();
- } else if (code == Constants.CODE_ALPHA_FROM_EMOJI) {
- setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
- }
- }
-
- static String shiftModeToString(final int shiftMode) {
- switch (shiftMode) {
- case UNSHIFT: return "UNSHIFT";
- case MANUAL_SHIFT: return "MANUAL";
- case AUTOMATIC_SHIFT: return "AUTOMATIC";
- default: return null;
- }
- }
-
- private static String switchStateToString(final int switchState) {
- switch (switchState) {
- case SWITCH_STATE_ALPHA: return "ALPHA";
- case SWITCH_STATE_SYMBOL_BEGIN: return "SYMBOL-BEGIN";
- case SWITCH_STATE_SYMBOL: return "SYMBOL";
- case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: return "MOMENTARY-ALPHA-SYMBOL";
- case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: return "MOMENTARY-SYMBOL-MORE";
- case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: return "MOMENTARY-ALPHA_SHIFT";
- default: return null;
- }
- }
-
- @Override
- public String toString() {
- return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString()
- : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS"))
- + " shift=" + mShiftKeyState
- + " symbol=" + mSymbolKeyState
- + " switch=" + switchStateToString(mSwitchState) + "]";
- }
-
- private String stateToString(final int autoCapsFlags, final int recapitalizeMode) {
- return this + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags)
- + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
deleted file mode 100644
index 0aaf6b401..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.TextUtils;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.RunInLocale;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-import java.util.Locale;
-
-// TODO: Make this an immutable class.
-public final class KeyboardTextsSet {
- public static final String PREFIX_TEXT = "!text/";
- private static final String PREFIX_RESOURCE = "!string/";
- public static final String SWITCH_TO_ALPHA_KEY_LABEL = "keylabel_to_alpha";
-
- private static final char BACKSLASH = Constants.CODE_BACKSLASH;
- private static final int MAX_REFERENCE_INDIRECTION = 10;
-
- private Resources mResources;
- private Locale mResourceLocale;
- private String mResourcePackageName;
- private String[] mTextsTable;
-
- public void setLocale(final Locale locale, final Context context) {
- final Resources res = context.getResources();
- // Null means the current system locale.
- final String resourcePackageName = res.getResourcePackageName(
- context.getApplicationInfo().labelRes);
- setLocale(locale, res, resourcePackageName);
- }
-
- @UsedForTesting
- public void setLocale(final Locale locale, final Resources res,
- final String resourcePackageName) {
- mResources = res;
- // Null means the current system locale.
- mResourceLocale = SubtypeLocaleUtils.NO_LANGUAGE.equals(locale.toString()) ? null : locale;
- mResourcePackageName = resourcePackageName;
- mTextsTable = KeyboardTextsTable.getTextsTable(locale);
- }
-
- public String getText(final String name) {
- return KeyboardTextsTable.getText(name, mTextsTable);
- }
-
- private static int searchTextNameEnd(final String text, final int start) {
- final int size = text.length();
- for (int pos = start; pos < size; pos++) {
- final char c = text.charAt(pos);
- // Label name should be consisted of [a-zA-Z_0-9].
- if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) {
- continue;
- }
- return pos;
- }
- return size;
- }
-
- // TODO: Resolve text reference when creating {@link KeyboardTextsTable} class.
- public String resolveTextReference(final String rawText) {
- if (TextUtils.isEmpty(rawText)) {
- return null;
- }
- int level = 0;
- String text = rawText;
- StringBuilder sb;
- do {
- level++;
- if (level >= MAX_REFERENCE_INDIRECTION) {
- throw new RuntimeException("Too many " + PREFIX_TEXT + " or " + PREFIX_RESOURCE +
- " reference indirection: " + text);
- }
-
- final int prefixLength = PREFIX_TEXT.length();
- final int size = text.length();
- if (size < prefixLength) {
- break;
- }
-
- sb = null;
- for (int pos = 0; pos < size; pos++) {
- final char c = text.charAt(pos);
- if (text.startsWith(PREFIX_TEXT, pos)) {
- if (sb == null) {
- sb = new StringBuilder(text.substring(0, pos));
- }
- pos = expandReference(text, pos, PREFIX_TEXT, sb);
- } else if (text.startsWith(PREFIX_RESOURCE, pos)) {
- if (sb == null) {
- sb = new StringBuilder(text.substring(0, pos));
- }
- pos = expandReference(text, pos, PREFIX_RESOURCE, sb);
- } else if (c == BACKSLASH) {
- if (sb != null) {
- // Append both escape character and escaped character.
- sb.append(text.substring(pos, Math.min(pos + 2, size)));
- }
- pos++;
- } else if (sb != null) {
- sb.append(c);
- }
- }
-
- if (sb != null) {
- text = sb.toString();
- }
- } while (sb != null);
- return TextUtils.isEmpty(text) ? null : text;
- }
-
- private int expandReference(final String text, final int pos, final String prefix,
- final StringBuilder sb) {
- final int prefixLength = prefix.length();
- final int end = searchTextNameEnd(text, pos + prefixLength);
- final String name = text.substring(pos + prefixLength, end);
- if (prefix.equals(PREFIX_TEXT)) {
- sb.append(getText(name));
- } else { // PREFIX_RESOURCE
- final String resourcePackageName = mResourcePackageName;
- final RunInLocale<String> getTextJob = new RunInLocale<String>() {
- @Override
- protected String job(final Resources res) {
- final int resId = res.getIdentifier(name, "string", resourcePackageName);
- return res.getString(resId);
- }
- };
- sb.append(getTextJob.runInLocale(mResources, mResourceLocale));
- }
- return end - 1;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
deleted file mode 100644
index 7dfb5328c..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ /dev/null
@@ -1,4198 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-/**
- * !!!!! DO NOT EDIT THIS FILE !!!!!
- *
- * This file is generated by tools/make-keyboard-text. The base template file is
- * tools/make-keyboard-text/res/src/com/android/inputmethod/keyboard/internal/
- * KeyboardTextsTable.tmpl
- *
- * This file must be updated when any text resources in keyboard layout files have been changed.
- * These text resources are referred as "!text/<resource_name>" in keyboard XML definitions,
- * and should be defined in
- * tools/make-keyboard-text/res/values-<locale>/donottranslate-more-keys.xml
- *
- * To update this file, please run the following commands.
- * $ cd $ANDROID_BUILD_TOP
- * $ mmm packages/inputmethods/LatinIME/tools/make-keyboard-text
- * $ make-keyboard-text -java packages/inputmethods/LatinIME/java
- *
- * The updated source file will be generated to the following path (this file).
- * packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/internal/
- * KeyboardTextsTable.java
- */
-public final class KeyboardTextsTable {
- // Name to index map.
- private static final HashMap<String, Integer> sNameToIndexesMap = new HashMap<>();
- // Locale to texts table map.
- private static final HashMap<String, String[]> sLocaleToTextsTableMap = new HashMap<>();
- // TODO: Remove this variable after debugging.
- // Texts table to locale maps.
- private static final HashMap<String[], String> sTextsTableToLocaleMap = new HashMap<>();
-
- public static String getText(final String name, final String[] textsTable) {
- final Integer indexObj = sNameToIndexesMap.get(name);
- if (indexObj == null) {
- throw new RuntimeException("Unknown text name=" + name + " locale="
- + sTextsTableToLocaleMap.get(textsTable));
- }
- final int index = indexObj;
- final String text = (index < textsTable.length) ? textsTable[index] : null;
- if (text != null) {
- return text;
- }
- // Validity check.
- if (index >= 0 && index < TEXTS_DEFAULT.length) {
- return TEXTS_DEFAULT[index];
- }
- // Throw exception for debugging purpose.
- throw new RuntimeException("Illegal index=" + index + " for name=" + name
- + " locale=" + sTextsTableToLocaleMap.get(textsTable));
- }
-
- public static String[] getTextsTable(final Locale locale) {
- final String localeKey = locale.toString();
- if (sLocaleToTextsTableMap.containsKey(localeKey)) {
- return sLocaleToTextsTableMap.get(localeKey);
- }
- final String languageKey = locale.getLanguage();
- if (sLocaleToTextsTableMap.containsKey(languageKey)) {
- return sLocaleToTextsTableMap.get(languageKey);
- }
- return TEXTS_DEFAULT;
- }
-
- private static final String[] NAMES = {
- // /* index:histogram */ "name",
- /* 0:33 */ "morekeys_a",
- /* 1:33 */ "morekeys_o",
- /* 2:32 */ "morekeys_e",
- /* 3:31 */ "morekeys_u",
- /* 4:31 */ "keylabel_to_alpha",
- /* 5:30 */ "morekeys_i",
- /* 6:25 */ "morekeys_n",
- /* 7:25 */ "morekeys_c",
- /* 8:23 */ "double_quotes",
- /* 9:22 */ "morekeys_s",
- /* 10:22 */ "single_quotes",
- /* 11:19 */ "keyspec_currency",
- /* 12:17 */ "morekeys_y",
- /* 13:16 */ "morekeys_z",
- /* 14:14 */ "morekeys_d",
- /* 15:10 */ "morekeys_t",
- /* 16:10 */ "morekeys_l",
- /* 17:10 */ "morekeys_g",
- /* 18: 9 */ "single_angle_quotes",
- /* 19: 9 */ "double_angle_quotes",
- /* 20: 8 */ "morekeys_r",
- /* 21: 6 */ "morekeys_k",
- /* 22: 6 */ "morekeys_cyrillic_ie",
- /* 23: 5 */ "keyspec_nordic_row1_11",
- /* 24: 5 */ "keyspec_nordic_row2_10",
- /* 25: 5 */ "keyspec_nordic_row2_11",
- /* 26: 5 */ "morekeys_nordic_row2_10",
- /* 27: 5 */ "keyspec_east_slavic_row1_9",
- /* 28: 5 */ "keyspec_east_slavic_row2_2",
- /* 29: 5 */ "keyspec_east_slavic_row2_11",
- /* 30: 5 */ "keyspec_east_slavic_row3_5",
- /* 31: 5 */ "morekeys_cyrillic_soft_sign",
- /* 32: 5 */ "keyspec_symbols_1",
- /* 33: 5 */ "keyspec_symbols_2",
- /* 34: 5 */ "keyspec_symbols_3",
- /* 35: 5 */ "keyspec_symbols_4",
- /* 36: 5 */ "keyspec_symbols_5",
- /* 37: 5 */ "keyspec_symbols_6",
- /* 38: 5 */ "keyspec_symbols_7",
- /* 39: 5 */ "keyspec_symbols_8",
- /* 40: 5 */ "keyspec_symbols_9",
- /* 41: 5 */ "keyspec_symbols_0",
- /* 42: 5 */ "keylabel_to_symbol",
- /* 43: 5 */ "additional_morekeys_symbols_1",
- /* 44: 5 */ "additional_morekeys_symbols_2",
- /* 45: 5 */ "additional_morekeys_symbols_3",
- /* 46: 5 */ "additional_morekeys_symbols_4",
- /* 47: 5 */ "additional_morekeys_symbols_5",
- /* 48: 5 */ "additional_morekeys_symbols_6",
- /* 49: 5 */ "additional_morekeys_symbols_7",
- /* 50: 5 */ "additional_morekeys_symbols_8",
- /* 51: 5 */ "additional_morekeys_symbols_9",
- /* 52: 5 */ "additional_morekeys_symbols_0",
- /* 53: 5 */ "morekeys_tablet_period",
- /* 54: 4 */ "morekeys_nordic_row2_11",
- /* 55: 4 */ "morekeys_punctuation",
- /* 56: 4 */ "keyspec_tablet_comma",
- /* 57: 4 */ "keyspec_period",
- /* 58: 4 */ "morekeys_period",
- /* 59: 4 */ "keyspec_tablet_period",
- /* 60: 3 */ "keyspec_swiss_row1_11",
- /* 61: 3 */ "keyspec_swiss_row2_10",
- /* 62: 3 */ "keyspec_swiss_row2_11",
- /* 63: 3 */ "morekeys_swiss_row1_11",
- /* 64: 3 */ "morekeys_swiss_row2_10",
- /* 65: 3 */ "morekeys_swiss_row2_11",
- /* 66: 3 */ "morekeys_star",
- /* 67: 3 */ "keyspec_left_parenthesis",
- /* 68: 3 */ "keyspec_right_parenthesis",
- /* 69: 3 */ "keyspec_left_square_bracket",
- /* 70: 3 */ "keyspec_right_square_bracket",
- /* 71: 3 */ "keyspec_left_curly_bracket",
- /* 72: 3 */ "keyspec_right_curly_bracket",
- /* 73: 3 */ "keyspec_less_than",
- /* 74: 3 */ "keyspec_greater_than",
- /* 75: 3 */ "keyspec_less_than_equal",
- /* 76: 3 */ "keyspec_greater_than_equal",
- /* 77: 3 */ "keyspec_left_double_angle_quote",
- /* 78: 3 */ "keyspec_right_double_angle_quote",
- /* 79: 3 */ "keyspec_left_single_angle_quote",
- /* 80: 3 */ "keyspec_right_single_angle_quote",
- /* 81: 3 */ "keyspec_comma",
- /* 82: 3 */ "morekeys_tablet_comma",
- /* 83: 3 */ "keyhintlabel_period",
- /* 84: 3 */ "morekeys_question",
- /* 85: 2 */ "morekeys_h",
- /* 86: 2 */ "morekeys_w",
- /* 87: 2 */ "morekeys_east_slavic_row2_2",
- /* 88: 2 */ "morekeys_cyrillic_u",
- /* 89: 2 */ "morekeys_cyrillic_en",
- /* 90: 2 */ "morekeys_cyrillic_ghe",
- /* 91: 2 */ "morekeys_cyrillic_o",
- /* 92: 2 */ "morekeys_cyrillic_i",
- /* 93: 2 */ "keyspec_south_slavic_row1_6",
- /* 94: 2 */ "keyspec_south_slavic_row2_11",
- /* 95: 2 */ "keyspec_south_slavic_row3_1",
- /* 96: 2 */ "keyspec_south_slavic_row3_8",
- /* 97: 2 */ "morekeys_tablet_punctuation",
- /* 98: 2 */ "keyspec_spanish_row2_10",
- /* 99: 2 */ "morekeys_bullet",
- /* 100: 2 */ "morekeys_left_parenthesis",
- /* 101: 2 */ "morekeys_right_parenthesis",
- /* 102: 2 */ "morekeys_arabic_diacritics",
- /* 103: 2 */ "keyhintlabel_tablet_comma",
- /* 104: 2 */ "keyhintlabel_tablet_period",
- /* 105: 2 */ "keyspec_symbols_question",
- /* 106: 2 */ "keyspec_symbols_semicolon",
- /* 107: 2 */ "keyspec_symbols_percent",
- /* 108: 2 */ "morekeys_symbols_semicolon",
- /* 109: 2 */ "morekeys_symbols_percent",
- /* 110: 2 */ "label_go_key",
- /* 111: 2 */ "label_send_key",
- /* 112: 2 */ "label_next_key",
- /* 113: 2 */ "label_done_key",
- /* 114: 2 */ "label_search_key",
- /* 115: 2 */ "label_previous_key",
- /* 116: 2 */ "label_pause_key",
- /* 117: 2 */ "label_wait_key",
- /* 118: 1 */ "morekeys_v",
- /* 119: 1 */ "morekeys_j",
- /* 120: 1 */ "morekeys_q",
- /* 121: 1 */ "morekeys_x",
- /* 122: 1 */ "keyspec_q",
- /* 123: 1 */ "keyspec_w",
- /* 124: 1 */ "keyspec_y",
- /* 125: 1 */ "keyspec_x",
- /* 126: 1 */ "morekeys_east_slavic_row2_11",
- /* 127: 1 */ "morekeys_cyrillic_ka",
- /* 128: 1 */ "morekeys_cyrillic_a",
- /* 129: 1 */ "morekeys_currency_dollar",
- /* 130: 1 */ "morekeys_plus",
- /* 131: 1 */ "morekeys_less_than",
- /* 132: 1 */ "morekeys_greater_than",
- /* 133: 1 */ "morekeys_exclamation",
- /* 134: 0 */ "morekeys_currency_generic",
- /* 135: 0 */ "morekeys_symbols_1",
- /* 136: 0 */ "morekeys_symbols_2",
- /* 137: 0 */ "morekeys_symbols_3",
- /* 138: 0 */ "morekeys_symbols_4",
- /* 139: 0 */ "morekeys_symbols_5",
- /* 140: 0 */ "morekeys_symbols_6",
- /* 141: 0 */ "morekeys_symbols_7",
- /* 142: 0 */ "morekeys_symbols_8",
- /* 143: 0 */ "morekeys_symbols_9",
- /* 144: 0 */ "morekeys_symbols_0",
- /* 145: 0 */ "morekeys_am_pm",
- /* 146: 0 */ "keyspec_settings",
- /* 147: 0 */ "keyspec_shortcut",
- /* 148: 0 */ "keyspec_action_next",
- /* 149: 0 */ "keyspec_action_previous",
- /* 150: 0 */ "keylabel_to_more_symbol",
- /* 151: 0 */ "keylabel_tablet_to_more_symbol",
- /* 152: 0 */ "keylabel_to_phone_numeric",
- /* 153: 0 */ "keylabel_to_phone_symbols",
- /* 154: 0 */ "keylabel_time_am",
- /* 155: 0 */ "keylabel_time_pm",
- /* 156: 0 */ "keyspec_popular_domain",
- /* 157: 0 */ "morekeys_popular_domain",
- /* 158: 0 */ "keyspecs_left_parenthesis_more_keys",
- /* 159: 0 */ "keyspecs_right_parenthesis_more_keys",
- /* 160: 0 */ "single_laqm_raqm",
- /* 161: 0 */ "single_raqm_laqm",
- /* 162: 0 */ "double_laqm_raqm",
- /* 163: 0 */ "double_raqm_laqm",
- /* 164: 0 */ "single_lqm_rqm",
- /* 165: 0 */ "single_9qm_lqm",
- /* 166: 0 */ "single_9qm_rqm",
- /* 167: 0 */ "single_rqm_9qm",
- /* 168: 0 */ "double_lqm_rqm",
- /* 169: 0 */ "double_9qm_lqm",
- /* 170: 0 */ "double_9qm_rqm",
- /* 171: 0 */ "double_rqm_9qm",
- /* 172: 0 */ "morekeys_single_quote",
- /* 173: 0 */ "morekeys_double_quote",
- /* 174: 0 */ "morekeys_tablet_double_quote",
- /* 175: 0 */ "keyspec_emoji_action_key",
- };
-
- private static final String EMPTY = "";
-
- /* Default texts */
- private static final String[] TEXTS_DEFAULT = {
- /* morekeys_a ~ */
- EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- /* keylabel_to_alpha */ "ABC",
- /* morekeys_i ~ */
- EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_lqm_rqm",
- /* morekeys_s */ EMPTY,
- /* single_quotes */ "!text/single_lqm_rqm",
- /* keyspec_currency */ "$",
- /* morekeys_y ~ */
- EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_g */
- /* single_angle_quotes */ "!text/single_laqm_raqm",
- /* double_angle_quotes */ "!text/double_laqm_raqm",
- /* morekeys_r ~ */
- EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_cyrillic_soft_sign */
- /* keyspec_symbols_1 */ "1",
- /* keyspec_symbols_2 */ "2",
- /* keyspec_symbols_3 */ "3",
- /* keyspec_symbols_4 */ "4",
- /* keyspec_symbols_5 */ "5",
- /* keyspec_symbols_6 */ "6",
- /* keyspec_symbols_7 */ "7",
- /* keyspec_symbols_8 */ "8",
- /* keyspec_symbols_9 */ "9",
- /* keyspec_symbols_0 */ "0",
- // Label for "switch to symbols" key.
- /* keylabel_to_symbol */ "?123",
- /* additional_morekeys_symbols_1 ~ */
- EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ additional_morekeys_symbols_0 */
- /* morekeys_tablet_period */ "!text/morekeys_tablet_punctuation",
- /* morekeys_nordic_row2_11 */ EMPTY,
- /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&",
- /* keyspec_tablet_comma */ ",",
- // Period key
- /* keyspec_period */ ".",
- /* morekeys_period */ "!text/morekeys_punctuation",
- /* keyspec_tablet_period */ ".",
- /* keyspec_swiss_row1_11 ~ */
- EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_swiss_row2_11 */
- // U+2020: "†" DAGGER
- // U+2021: "‡" DOUBLE DAGGER
- // U+2605: "★" BLACK STAR
- /* morekeys_star */ "\u2020,\u2021,\u2605",
- // The all letters need to be mirrored are found at
- // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
- // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- // U+2264: "≤" LESS-THAN OR EQUAL TO
- // U+2265: "≥" GREATER-THAN EQUAL TO
- // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- /* keyspec_left_parenthesis */ "(",
- /* keyspec_right_parenthesis */ ")",
- /* keyspec_left_square_bracket */ "[",
- /* keyspec_right_square_bracket */ "]",
- /* keyspec_left_curly_bracket */ "{",
- /* keyspec_right_curly_bracket */ "}",
- /* keyspec_less_than */ "<",
- /* keyspec_greater_than */ ">",
- /* keyspec_less_than_equal */ "\u2264",
- /* keyspec_greater_than_equal */ "\u2265",
- /* keyspec_left_double_angle_quote */ "\u00AB",
- /* keyspec_right_double_angle_quote */ "\u00BB",
- /* keyspec_left_single_angle_quote */ "\u2039",
- /* keyspec_right_single_angle_quote */ "\u203A",
- // Comma key
- /* keyspec_comma */ ",",
- /* morekeys_tablet_comma */ EMPTY,
- /* keyhintlabel_period */ EMPTY,
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* morekeys_question */ "\u00BF",
- /* morekeys_h ~ */
- EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ keyspec_south_slavic_row3_8 */
- /* morekeys_tablet_punctuation */ "!autoColumnOrder!7,\\,,',#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,@,:,-,\",+,\\%,&",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* keyspec_spanish_row2_10 */ "\u00F1",
- // U+266A: "♪" EIGHTH NOTE
- // U+2665: "♥" BLACK HEART SUIT
- // U+2660: "♠" BLACK SPADE SUIT
- // U+2666: "♦" BLACK DIAMOND SUIT
- // U+2663: "♣" BLACK CLUB SUIT
- /* morekeys_bullet */ "\u266A,\u2665,\u2660,\u2666,\u2663",
- /* morekeys_left_parenthesis */ "!fixedColumnOrder!3,!text/keyspecs_left_parenthesis_more_keys",
- /* morekeys_right_parenthesis */ "!fixedColumnOrder!3,!text/keyspecs_right_parenthesis_more_keys",
- /* morekeys_arabic_diacritics ~ */
- EMPTY, EMPTY, EMPTY,
- /* ~ keyhintlabel_tablet_period */
- /* keyspec_symbols_question */ "?",
- /* keyspec_symbols_semicolon */ ";",
- /* keyspec_symbols_percent */ "%",
- /* morekeys_symbols_semicolon */ EMPTY,
- // U+2030: "‰" PER MILLE SIGN
- /* morekeys_symbols_percent */ "\u2030",
- /* label_go_key */ "!string/label_go_key",
- /* label_send_key */ "!string/label_send_key",
- /* label_next_key */ "!string/label_next_key",
- /* label_done_key */ "!string/label_done_key",
- /* label_search_key */ "!string/label_search_key",
- /* label_previous_key */ "!string/label_previous_key",
- /* label_pause_key */ "!string/label_pause_key",
- /* label_wait_key */ "!string/label_wait_key",
- /* morekeys_v ~ */
- EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_x */
- /* keyspec_q */ "q",
- /* keyspec_w */ "w",
- /* keyspec_y */ "y",
- /* keyspec_x */ "x",
- /* morekeys_east_slavic_row2_11 ~ */
- EMPTY, EMPTY, EMPTY,
- /* ~ morekeys_cyrillic_a */
- // U+00A2: "¢" CENT SIGN
- // U+00A3: "£" POUND SIGN
- // U+20AC: "€" EURO SIGN
- // U+00A5: "¥" YEN SIGN
- // U+20B1: "₱" PESO SIGN
- /* morekeys_currency_dollar */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
- // U+00B1: "±" PLUS-MINUS SIGN
- /* morekeys_plus */ "\u00B1",
- /* morekeys_less_than */ "!fixedColumnOrder!3,!text/keyspec_left_single_angle_quote,!text/keyspec_less_than_equal,!text/keyspec_left_double_angle_quote",
- /* morekeys_greater_than */ "!fixedColumnOrder!3,!text/keyspec_right_single_angle_quote,!text/keyspec_greater_than_equal,!text/keyspec_right_double_angle_quote",
- // U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* morekeys_exclamation */ "\u00A1",
- /* morekeys_currency_generic */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1",
- // U+00B9: "¹" SUPERSCRIPT ONE
- // U+00BD: "½" VULGAR FRACTION ONE HALF
- // U+2153: "⅓" VULGAR FRACTION ONE THIRD
- // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
- // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
- /* morekeys_symbols_1 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
- // U+00B2: "²" SUPERSCRIPT TWO
- // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
- /* morekeys_symbols_2 */ "\u00B2,\u2154",
- // U+00B3: "³" SUPERSCRIPT THREE
- // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
- // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
- /* morekeys_symbols_3 */ "\u00B3,\u00BE,\u215C",
- // U+2074: "⁴" SUPERSCRIPT FOUR
- /* morekeys_symbols_4 */ "\u2074",
- // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
- /* morekeys_symbols_5 */ "\u215D",
- /* morekeys_symbols_6 */ EMPTY,
- // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
- /* morekeys_symbols_7 */ "\u215E",
- /* morekeys_symbols_8 */ EMPTY,
- /* morekeys_symbols_9 */ EMPTY,
- // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
- // U+2205: "∅" EMPTY SET
- /* morekeys_symbols_0 */ "\u207F,\u2205",
- /* morekeys_am_pm */ "!fixedColumnOrder!2,!hasLabels!,!text/keylabel_time_am,!text/keylabel_time_pm",
- /* keyspec_settings */ "!icon/settings_key|!code/key_settings",
- /* keyspec_shortcut */ "!icon/shortcut_key|!code/key_shortcut",
- /* keyspec_action_next */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
- /* keyspec_action_previous */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
- // Label for "switch to more symbol" modifier key ("= \ <"). Must be short to fit on key!
- /* keylabel_to_more_symbol */ "= \\\\ <",
- // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
- /* keylabel_tablet_to_more_symbol */ "~ [ <",
- // Label for "switch to phone numeric" key. Must be short to fit on key!
- /* keylabel_to_phone_numeric */ "123",
- // Label for "switch to phone symbols" key. Must be short to fit on key!
- // U+FF0A: "*" FULLWIDTH ASTERISK
- // U+FF03: "#" FULLWIDTH NUMBER SIGN
- /* keylabel_to_phone_symbols */ "\uFF0A\uFF03",
- // Key label for "ante meridiem"
- /* keylabel_time_am */ "AM",
- // Key label for "post meridiem"
- /* keylabel_time_pm */ "PM",
- /* keyspec_popular_domain */ ".com",
- // popular web domains for the locale - most popular, displayed on the keyboard
- /* morekeys_popular_domain */ "!hasLabels!,.net,.org,.gov,.edu",
- /* keyspecs_left_parenthesis_more_keys */ "!text/keyspec_less_than,!text/keyspec_left_curly_bracket,!text/keyspec_left_square_bracket",
- /* keyspecs_right_parenthesis_more_keys */ "!text/keyspec_greater_than,!text/keyspec_right_curly_bracket,!text/keyspec_right_square_bracket",
- // The following characters don't need BIDI mirroring.
- // U+2018: "‘" LEFT SINGLE QUOTATION MARK
- // U+2019: "’" RIGHT SINGLE QUOTATION MARK
- // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
- // U+201C: "“" LEFT DOUBLE QUOTATION MARK
- // U+201D: "”" RIGHT DOUBLE QUOTATION MARK
- // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
- // Abbreviations are:
- // laqm: LEFT-POINTING ANGLE QUOTATION MARK
- // raqm: RIGHT-POINTING ANGLE QUOTATION MARK
- // lqm: LEFT QUOTATION MARK
- // rqm: RIGHT QUOTATION MARK
- // 9qm: LOW-9 QUOTATION MARK
- // The following each quotation mark pair consist of
- // <opening quotation mark>, <closing quotation mark>
- // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>.
- /* single_laqm_raqm */ "!text/keyspec_left_single_angle_quote,!text/keyspec_right_single_angle_quote",
- /* single_raqm_laqm */ "!text/keyspec_right_single_angle_quote,!text/keyspec_left_single_angle_quote",
- /* double_laqm_raqm */ "!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote",
- /* double_raqm_laqm */ "!text/keyspec_right_double_angle_quote,!text/keyspec_left_double_angle_quote",
- // The following each quotation mark triplet consists of
- // <another quotation mark>, <opening quotation mark>, <closing quotation mark>
- // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>.
- /* single_lqm_rqm */ "\u201A,\u2018,\u2019",
- /* single_9qm_lqm */ "\u2019,\u201A,\u2018",
- /* single_9qm_rqm */ "\u2018,\u201A,\u2019",
- /* single_rqm_9qm */ "\u2018,\u2019,\u201A",
- /* double_lqm_rqm */ "\u201E,\u201C,\u201D",
- /* double_9qm_lqm */ "\u201D,\u201E,\u201C",
- /* double_9qm_rqm */ "\u201C,\u201E,\u201D",
- /* double_rqm_9qm */ "\u201C,\u201D,\u201E",
- /* morekeys_single_quote */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
- /* morekeys_double_quote */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
- /* morekeys_tablet_double_quote */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
- /* keyspec_emoji_action_key */ "!icon/emoji_action_key|!code/key_emoji",
- };
-
- /* Locale af: Afrikaans */
- private static final String[] TEXTS_af = {
- // This is the same as Dutch except more keys of y and demoting vowels with diaeresis.
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E1,\u00E2,\u00E4,\u00E0,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F3,\u00F4,\u00F6,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_i */ "\u00ED,\u00EC,\u00EF,\u00EE,\u012F,\u012B,\u0133",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- /* morekeys_c ~ */
- null, null, null, null, null,
- /* ~ keyspec_currency */
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_y */ "\u00FD,\u0133",
- };
-
- /* Locale ar: Arabic */
- private static final String[] TEXTS_ar = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE
- // U+200C: ZERO WIDTH NON-JOINER
- // U+0628: "ب" ARABIC LETTER BEH
- // U+062C: "ج" ARABIC LETTER JEEM
- /* keylabel_to_alpha */ "\u0623\u200C\u0628\u200C\u062C",
- /* morekeys_i ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_cyrillic_soft_sign */
- // U+0661: "١" ARABIC-INDIC DIGIT ONE
- /* keyspec_symbols_1 */ "\u0661",
- // U+0662: "٢" ARABIC-INDIC DIGIT TWO
- /* keyspec_symbols_2 */ "\u0662",
- // U+0663: "٣" ARABIC-INDIC DIGIT THREE
- /* keyspec_symbols_3 */ "\u0663",
- // U+0664: "٤" ARABIC-INDIC DIGIT FOUR
- /* keyspec_symbols_4 */ "\u0664",
- // U+0665: "٥" ARABIC-INDIC DIGIT FIVE
- /* keyspec_symbols_5 */ "\u0665",
- // U+0666: "٦" ARABIC-INDIC DIGIT SIX
- /* keyspec_symbols_6 */ "\u0666",
- // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
- /* keyspec_symbols_7 */ "\u0667",
- // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
- /* keyspec_symbols_8 */ "\u0668",
- // U+0669: "٩" ARABIC-INDIC DIGIT NINE
- /* keyspec_symbols_9 */ "\u0669",
- // U+0660: "٠" ARABIC-INDIC DIGIT ZERO
- /* keyspec_symbols_0 */ "\u0660",
- // Label for "switch to symbols" key.
- // U+061F: "؟" ARABIC QUESTION MARK
- /* keylabel_to_symbol */ "\u0663\u0662\u0661\u061F",
- /* additional_morekeys_symbols_1 */ "1",
- /* additional_morekeys_symbols_2 */ "2",
- /* additional_morekeys_symbols_3 */ "3",
- /* additional_morekeys_symbols_4 */ "4",
- /* additional_morekeys_symbols_5 */ "5",
- /* additional_morekeys_symbols_6 */ "6",
- /* additional_morekeys_symbols_7 */ "7",
- /* additional_morekeys_symbols_8 */ "8",
- /* additional_morekeys_symbols_9 */ "9",
- // U+066B: "٫" ARABIC DECIMAL SEPARATOR
- // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C",
- /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
- /* morekeys_nordic_row2_11 */ null,
- /* morekeys_punctuation */ null,
- // U+061F: "؟" ARABIC QUESTION MARK
- // U+060C: "،" ARABIC COMMA
- // U+061B: "؛" ARABIC SEMICOLON
- /* keyspec_tablet_comma */ "\u060C",
- /* keyspec_period */ null,
- /* morekeys_period */ "!text/morekeys_arabic_diacritics",
- /* keyspec_tablet_period ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_swiss_row2_11 */
- // U+2605: "★" BLACK STAR
- // U+066D: "٭" ARABIC FIVE POINTED STAR
- /* morekeys_star */ "\u2605,\u066D",
- // U+2264: "≤" LESS-THAN OR EQUAL TO
- // U+2265: "≥" GREATER-THAN EQUAL TO
- // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
- // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- /* keyspec_left_parenthesis */ "(|)",
- /* keyspec_right_parenthesis */ ")|(",
- /* keyspec_left_square_bracket */ "[|]",
- /* keyspec_right_square_bracket */ "]|[",
- /* keyspec_left_curly_bracket */ "{|}",
- /* keyspec_right_curly_bracket */ "}|{",
- /* keyspec_less_than */ "<|>",
- /* keyspec_greater_than */ ">|<",
- /* keyspec_less_than_equal */ "\u2264|\u2265",
- /* keyspec_greater_than_equal */ "\u2265|\u2264",
- /* keyspec_left_double_angle_quote */ "\u00AB|\u00BB",
- /* keyspec_right_double_angle_quote */ "\u00BB|\u00AB",
- /* keyspec_left_single_angle_quote */ "\u2039|\u203A",
- /* keyspec_right_single_angle_quote */ "\u203A|\u2039",
- // U+060C: "،" ARABIC COMMA
- /* keyspec_comma */ "\u060C",
- /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,\",\'",
- // U+0651: "ّ" ARABIC SHADDA
- /* keyhintlabel_period */ "\u0651",
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* morekeys_question */ "?,\u00BF",
- /* morekeys_h ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ keyspec_spanish_row2_10 */
- // U+266A: "♪" EIGHTH NOTE
- /* morekeys_bullet */ "\u266A",
- // The all letters need to be mirrored are found at
- // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
- // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* morekeys_left_parenthesis */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,!text/keyspecs_left_parenthesis_more_keys",
- /* morekeys_right_parenthesis */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,!text/keyspecs_right_parenthesis_more_keys",
- // U+0655: "ٕ" ARABIC HAMZA BELOW
- // U+0654: "ٔ" ARABIC HAMZA ABOVE
- // U+0652: "ْ" ARABIC SUKUN
- // U+064D: "ٍ" ARABIC KASRATAN
- // U+064C: "ٌ" ARABIC DAMMATAN
- // U+064B: "ً" ARABIC FATHATAN
- // U+0651: "ّ" ARABIC SHADDA
- // U+0656: "ٖ" ARABIC SUBSCRIPT ALEF
- // U+0670: "ٰ" ARABIC LETTER SUPERSCRIPT ALEF
- // U+0653: "ٓ" ARABIC MADDAH ABOVE
- // U+0650: "ِ" ARABIC KASRA
- // U+064F: "ُ" ARABIC DAMMA
- // U+064E: "َ" ARABIC FATHA
- // U+0640: "ـ" ARABIC TATWEEL
- // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
- /* morekeys_arabic_diacritics */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
- /* keyhintlabel_tablet_comma */ "\u061F",
- /* keyhintlabel_tablet_period */ "\u0651",
- /* keyspec_symbols_question */ "\u061F",
- /* keyspec_symbols_semicolon */ "\u061B",
- // U+066A: "٪" ARABIC PERCENT SIGN
- /* keyspec_symbols_percent */ "\u066A",
- /* morekeys_symbols_semicolon */ ";",
- // U+2030: "‰" PER MILLE SIGN
- /* morekeys_symbols_percent */ "\\%,\u2030",
- };
-
- /* Locale az_AZ: Azerbaijani (Azerbaijan) */
- private static final String[] TEXTS_az_AZ = {
- // This is the same as Turkish
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- /* morekeys_a */ "\u00E2,\u00E4,\u00E1",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D",
- // U+0259: "ə" LATIN SMALL LETTER SCHWA
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* morekeys_e */ "\u0259,\u00E9",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
- /* keylabel_to_alpha */ null,
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u0148,\u00F1",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- /* double_quotes */ null,
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
- /* single_quotes */ null,
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- /* morekeys_y */ "\u00FD",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017E",
- /* morekeys_d ~ */
- null, null, null,
- /* ~ morekeys_l */
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u011F",
- };
-
- /* Locale be_BY: Belarusian (Belarus) */
- private static final String[] TEXTS_be_BY = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_k */
- // U+0451: "ё" CYRILLIC SMALL LETTER IO
- /* morekeys_cyrillic_ie */ "\u0451",
- /* keyspec_nordic_row1_11 ~ */
- null, null, null, null,
- /* ~ morekeys_nordic_row2_10 */
- // U+045E: "ў" CYRILLIC SMALL LETTER SHORT U
- /* keyspec_east_slavic_row1_9 */ "\u045E",
- // U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* keyspec_east_slavic_row2_2 */ "\u044B",
- // U+044D: "э" CYRILLIC SMALL LETTER E
- /* keyspec_east_slavic_row2_11 */ "\u044D",
- // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* keyspec_east_slavic_row3_5 */ "\u0456",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* morekeys_cyrillic_soft_sign */ "\u044A",
- };
-
- /* Locale bg: Bulgarian */
- private static final String[] TEXTS_bg = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- // single_quotes of Bulgarian is default single_quotes_right_left.
- /* double_quotes */ "!text/double_9qm_lqm",
- };
-
- /* Locale bn_BD: Bengali (Bangladesh) */
- private static final String[] TEXTS_bn_BD = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0995: "क" BENGALI LETTER KA
- // U+0996: "ख" BENGALI LETTER KHA
- // U+0997: "ग" BENGALI LETTER GA
- /* keylabel_to_alpha */ "\u0995\u0996\u0997",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+09F3: "৳" BENGALI RUPEE SIGN
- /* keyspec_currency */ "\u09F3",
- };
-
- /* Locale bn_IN: Bengali (India) */
- private static final String[] TEXTS_bn_IN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0995: "क" BENGALI LETTER KA
- // U+0996: "ख" BENGALI LETTER KHA
- // U+0997: "ग" BENGALI LETTER GA
- /* keylabel_to_alpha */ "\u0995\u0996\u0997",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- };
-
- /* Locale ca: Catalan */
- private static final String[] TEXTS_ca = {
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E0,\u00E1,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F2,\u00F3,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E8,\u00E9,\u00EB,\u00EA,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- /* double_quotes ~ */
- null, null, null, null, null, null, null, null,
- /* ~ morekeys_t */
- // U+00B7: "·" MIDDLE DOT
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "l\u00B7l,\u0142",
- /* morekeys_g ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null,
- /* ~ morekeys_nordic_row2_11 */
- // U+00B7: "·" MIDDLE DOT
- /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,\u00B7,#,),(,/,;,',@,:,-,\",+,\\%,&",
- /* keyspec_tablet_comma ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ keyspec_south_slavic_row3_8 */
- /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,\\,,',\u00B7,#,),(,/,;,@,:,-,\",+,\\%,&",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- /* keyspec_spanish_row2_10 */ "\u00E7",
- };
-
- /* Locale cs: Czech */
- private static final String[] TEXTS_cs = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E1,\u00E0,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u011B,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u00EC,\u012F,\u012B",
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u0148,\u00F1,\u0144",
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u00E7,\u0107",
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- /* morekeys_s */ "\u0161,\u00DF,\u015B",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- /* morekeys_z */ "\u017E,\u017A,\u017C",
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- /* morekeys_d */ "\u010F",
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- /* morekeys_t */ "\u0165",
- /* morekeys_l */ null,
- /* morekeys_g */ null,
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- /* morekeys_r */ "\u0159",
- };
-
- /* Locale da: Danish */
- private static final String[] TEXTS_da = {
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E5,\u00E6,\u00E1,\u00E4,\u00E0,\u00E2,\u00E3,\u0101",
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F8,\u00F6,\u00F3,\u00F4,\u00F2,\u00F5,\u0153,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- /* morekeys_e */ "\u00E9,\u00EB",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- /* morekeys_i */ "\u00ED,\u00EF",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- /* morekeys_c */ null,
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u00DF,\u015B,\u0161",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- /* morekeys_z */ null,
- // U+00F0: "ð" LATIN SMALL LETTER ETH
- /* morekeys_d */ "\u00F0",
- /* morekeys_t */ null,
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "\u0142",
- /* morekeys_g */ null,
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- /* morekeys_r ~ */
- null, null, null,
- /* ~ morekeys_cyrillic_ie */
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- /* keyspec_nordic_row1_11 */ "\u00E5",
- // U+00E6: "æ" LATIN SMALL LETTER AE
- /* keyspec_nordic_row2_10 */ "\u00E6",
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* keyspec_nordic_row2_11 */ "\u00F8",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* morekeys_nordic_row2_10 */ "\u00E4",
- /* keyspec_east_slavic_row1_9 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_tablet_period */
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* morekeys_nordic_row2_11 */ "\u00F6",
- };
-
- /* Locale de: German */
- private static final String[] TEXTS_de = {
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E4,%,\u00E2,\u00E0,\u00E1,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F6,%,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u00F8,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0117",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FC,%,\u00FB,\u00F9,\u00FA,\u016B",
- /* keylabel_to_alpha */ null,
- /* morekeys_i */ null,
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- /* morekeys_c */ null,
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u00DF,\u015B,\u0161",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_g */
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- /* morekeys_r ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null,
- /* ~ keyspec_tablet_period */
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* keyspec_swiss_row1_11 */ "\u00FC",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* keyspec_swiss_row2_10 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* keyspec_swiss_row2_11 */ "\u00E4",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- /* morekeys_swiss_row1_11 */ "\u00E8",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* morekeys_swiss_row2_10 */ "\u00E9",
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- /* morekeys_swiss_row2_11 */ "\u00E0",
- };
-
- /* Locale el: Greek */
- private static final String[] TEXTS_el = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0391: "Α" GREEK CAPITAL LETTER ALPHA
- // U+0392: "Β" GREEK CAPITAL LETTER BETA
- // U+0393: "Γ" GREEK CAPITAL LETTER GAMMA
- /* keylabel_to_alpha */ "\u0391\u0392\u0393",
- };
-
- /* Locale en: English */
- private static final String[] TEXTS_en = {
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- /* morekeys_o */ "\u00F3,\u00F4,\u00F6,\u00F2,\u0153,\u00F8,\u014D,\u00F5",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u00F1",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- /* morekeys_c */ "\u00E7",
- /* double_quotes */ null,
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- /* morekeys_s */ "\u00DF",
- };
-
- /* Locale eo: Esperanto */
- private static final String[] TEXTS_eo = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E1,\u00E0,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101,\u0103,\u0105,\u00AA",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D,\u0151,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u011B,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- // U+00B5: "µ" MICRO SIGN
- /* morekeys_u */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B,\u0169,\u0171,\u0173,\u00B5",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u0129,\u00EC,\u012F,\u012B,\u0131,\u0133",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
- // U+014B: "ŋ" LATIN SMALL LETTER ENG
- /* morekeys_n */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B",
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
- /* morekeys_c */ "\u0107,\u010D,\u00E7,\u010B",
- /* double_quotes */ null,
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- /* morekeys_s */ "\u00DF,\u0161,\u015B,\u0219,\u015F",
- /* single_quotes */ null,
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- // U+00FE: "þ" LATIN SMALL LETTER THORN
- /* morekeys_y */ "y,\u00FD,\u0177,\u00FF,\u00FE",
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017A,\u017C,\u017E",
- // U+00F0: "ð" LATIN SMALL LETTER ETH
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
- /* morekeys_d */ "\u00F0,\u010F,\u0111",
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- // U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW
- // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
- /* morekeys_t */ "\u0165,\u021B,\u0163,\u0167",
- // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "\u013A,\u013C,\u013E,\u0140,\u0142",
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
- // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
- /* morekeys_g */ "\u011F,\u0121,\u0123",
- /* single_angle_quotes */ null,
- /* double_angle_quotes */ null,
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
- /* morekeys_r */ "\u0159,\u0155,\u0157",
- // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- // U+0138: "ĸ" LATIN SMALL LETTER KRA
- /* morekeys_k */ "\u0137,\u0138",
- /* morekeys_cyrillic_ie ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null,
- /* ~ morekeys_question */
- // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
- // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE
- /* morekeys_h */ "\u0125,\u0127",
- // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
- /* morekeys_w */ "w,\u0175",
- /* morekeys_east_slavic_row2_2 ~ */
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_tablet_punctuation */
- // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
- /* keyspec_spanish_row2_10 */ "\u0135",
- /* morekeys_bullet ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null,
- /* ~ label_wait_key */
- // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
- /* morekeys_v */ "w,\u0175",
- /* morekeys_j */ null,
- /* morekeys_q */ "q",
- /* morekeys_x */ "x",
- // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
- /* keyspec_q */ "\u015D",
- // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
- /* keyspec_w */ "\u011D",
- // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
- /* keyspec_y */ "\u016D",
- // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
- /* keyspec_x */ "\u0109",
- };
-
- /* Locale es: Spanish */
- private static final String[] TEXTS_es = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- /* double_quotes ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~ morekeys_nordic_row2_11 */
- // U+00A1: "¡" INVERTED EXCLAMATION MARK
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,#,),(,/,;,\u00A1,',@,:,-,\",+,\\%,&,\u00BF",
- };
-
- /* Locale et_EE: Estonian (Estonia) */
- private static final String[] TEXTS_et_EE = {
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- /* morekeys_a */ "\u00E4,\u0101,\u00E0,\u00E1,\u00E2,\u00E3,\u00E5,\u00E6,\u0105",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* morekeys_o */ "\u00F6,\u00F5,\u00F2,\u00F3,\u00F4,\u0153,\u0151,\u00F8",
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- /* morekeys_e */ "\u0113,\u00E8,\u0117,\u00E9,\u00EA,\u00EB,\u0119,\u011B",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- /* morekeys_u */ "\u00FC,\u016B,\u0173,\u00F9,\u00FA,\u00FB,\u016F,\u0171",
- /* keylabel_to_alpha */ null,
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- /* morekeys_i */ "\u012B,\u00EC,\u012F,\u00ED,\u00EE,\u00EF,\u0131",
- // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u0146,\u00F1,\u0144",
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u00E7,\u0107",
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- /* morekeys_z */ "\u017E,\u017C,\u017A",
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- /* morekeys_d */ "\u010F",
- // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- /* morekeys_t */ "\u0163,\u0165",
- // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- /* morekeys_l */ "\u013C,\u0142,\u013A,\u013E",
- // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u0123,\u011F",
- /* single_angle_quotes */ null,
- /* double_angle_quotes */ null,
- // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- /* morekeys_r */ "\u0157,\u0159,\u0155",
- // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- /* morekeys_k */ "\u0137",
- /* morekeys_cyrillic_ie */ null,
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* keyspec_nordic_row1_11 */ "\u00FC",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* keyspec_nordic_row2_10 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* keyspec_nordic_row2_11 */ "\u00E4",
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- /* morekeys_nordic_row2_10 */ "\u00F5",
- };
-
- /* Locale eu_ES: Basque (Spain) */
- private static final String[] TEXTS_eu_ES = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- };
-
- /* Locale fa: Persian */
- private static final String[] TEXTS_fa = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0627: "ا" ARABIC LETTER ALEF
- // U+200C: ZERO WIDTH NON-JOINER
- // U+0628: "ب" ARABIC LETTER BEH
- // U+067E: "پ" ARABIC LETTER PEH
- /* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+FDFC: "﷼" RIAL SIGN
- /* keyspec_currency */ "\uFDFC",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~ morekeys_cyrillic_soft_sign */
- // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
- /* keyspec_symbols_1 */ "\u06F1",
- // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
- /* keyspec_symbols_2 */ "\u06F2",
- // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
- /* keyspec_symbols_3 */ "\u06F3",
- // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
- /* keyspec_symbols_4 */ "\u06F4",
- // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
- /* keyspec_symbols_5 */ "\u06F5",
- // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
- /* keyspec_symbols_6 */ "\u06F6",
- // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
- /* keyspec_symbols_7 */ "\u06F7",
- // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
- /* keyspec_symbols_8 */ "\u06F8",
- // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
- /* keyspec_symbols_9 */ "\u06F9",
- // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
- /* keyspec_symbols_0 */ "\u06F0",
- // Label for "switch to symbols" key.
- // U+061F: "؟" ARABIC QUESTION MARK
- /* keylabel_to_symbol */ "\u06F3\u06F2\u06F1\u061F",
- /* additional_morekeys_symbols_1 */ "1",
- /* additional_morekeys_symbols_2 */ "2",
- /* additional_morekeys_symbols_3 */ "3",
- /* additional_morekeys_symbols_4 */ "4",
- /* additional_morekeys_symbols_5 */ "5",
- /* additional_morekeys_symbols_6 */ "6",
- /* additional_morekeys_symbols_7 */ "7",
- /* additional_morekeys_symbols_8 */ "8",
- /* additional_morekeys_symbols_9 */ "9",
- // U+066B: "٫" ARABIC DECIMAL SEPARATOR
- // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C",
- /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
- /* morekeys_nordic_row2_11 */ null,
- /* morekeys_punctuation */ null,
- // U+060C: "،" ARABIC COMMA
- // U+061B: "؛" ARABIC SEMICOLON
- // U+061F: "؟" ARABIC QUESTION MARK
- // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- /* keyspec_tablet_comma */ "\u060C",
- /* keyspec_period */ null,
- /* morekeys_period */ "!text/morekeys_arabic_diacritics",
- /* keyspec_tablet_period ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_swiss_row2_11 */
- // U+2605: "★" BLACK STAR
- // U+066D: "٭" ARABIC FIVE POINTED STAR
- /* morekeys_star */ "\u2605,\u066D",
- /* keyspec_left_parenthesis */ "(|)",
- /* keyspec_right_parenthesis */ ")|(",
- /* keyspec_left_square_bracket */ "[|]",
- /* keyspec_right_square_bracket */ "]|[",
- /* keyspec_left_curly_bracket */ "{|}",
- /* keyspec_right_curly_bracket */ "}|{",
- /* keyspec_less_than */ "<|>",
- /* keyspec_greater_than */ ">|<",
- /* keyspec_less_than_equal */ "\u2264|\u2265",
- /* keyspec_greater_than_equal */ "\u2265|\u2264",
- /* keyspec_left_double_angle_quote */ "\u00AB|\u00BB",
- /* keyspec_right_double_angle_quote */ "\u00BB|\u00AB",
- /* keyspec_left_single_angle_quote */ "\u2039|\u203A",
- /* keyspec_right_single_angle_quote */ "\u203A|\u2039",
- // U+060C: "،" ARABIC COMMA
- /* keyspec_comma */ "\u060C",
- /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote",
- // U+064B: "ً" ARABIC FATHATAN
- /* keyhintlabel_period */ "\u064B",
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* morekeys_question */ "?,\u00BF",
- /* morekeys_h ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ keyspec_spanish_row2_10 */
- // U+266A: "♪" EIGHTH NOTE
- /* morekeys_bullet */ "\u266A",
- // The all letters need to be mirrored are found at
- // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
- // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* morekeys_left_parenthesis */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,!text/keyspecs_left_parenthesis_more_keys",
- /* morekeys_right_parenthesis */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,!text/keyspecs_right_parenthesis_more_keys",
- // U+0655: "ٕ" ARABIC HAMZA BELOW
- // U+0652: "ْ" ARABIC SUKUN
- // U+0651: "ّ" ARABIC SHADDA
- // U+064C: "ٌ" ARABIC DAMMATAN
- // U+064D: "ٍ" ARABIC KASRATAN
- // U+064B: "ً" ARABIC FATHATAN
- // U+0654: "ٔ" ARABIC HAMZA ABOVE
- // U+0656: "ٖ" ARABIC SUBSCRIPT ALEF
- // U+0670: "ٰ" ARABIC LETTER SUPERSCRIPT ALEF
- // U+0653: "ٓ" ARABIC MADDAH ABOVE
- // U+064F: "ُ" ARABIC DAMMA
- // U+0650: "ِ" ARABIC KASRA
- // U+064E: "َ" ARABIC FATHA
- // U+0640: "ـ" ARABIC TATWEEL
- // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
- /* morekeys_arabic_diacritics */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
- /* keyhintlabel_tablet_comma */ "\u061F",
- /* keyhintlabel_tablet_period */ "\u064B",
- /* keyspec_symbols_question */ "\u061F",
- /* keyspec_symbols_semicolon */ "\u061B",
- // U+066A: "٪" ARABIC PERCENT SIGN
- /* keyspec_symbols_percent */ "\u066A",
- /* morekeys_symbols_semicolon */ ";",
- // U+2030: "‰" PER MILLE SIGN
- /* morekeys_symbols_percent */ "\\%,\u2030",
- /* label_go_key ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~ morekeys_plus */
- // U+2264: "≤" LESS-THAN OR EQUAL TO
- // U+2265: "≥" GREATER-THAN EQUAL TO
- // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
- // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- /* morekeys_less_than */ "!fixedColumnOrder!3,!text/keyspec_left_single_angle_quote,!text/keyspec_less_than_equal,!text/keyspec_less_than",
- /* morekeys_greater_than */ "!fixedColumnOrder!3,!text/keyspec_right_single_angle_quote,!text/keyspec_greater_than_equal,!text/keyspec_greater_than",
- };
-
- /* Locale fi: Finnish */
- private static final String[] TEXTS_fi = {
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E4,\u00E5,\u00E6,\u00E0,\u00E1,\u00E2,\u00E3,\u0101",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F6,\u00F8,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u014D",
- /* morekeys_e */ null,
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* morekeys_u */ "\u00FC",
- /* keylabel_to_alpha ~ */
- null, null, null, null, null,
- /* ~ double_quotes */
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- /* morekeys_s */ "\u0161,\u00DF,\u015B",
- /* single_quotes ~ */
- null, null, null,
- /* ~ morekeys_y */
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- /* morekeys_z */ "\u017E,\u017A,\u017C",
- /* morekeys_d ~ */
- null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_cyrillic_ie */
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- /* keyspec_nordic_row1_11 */ "\u00E5",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* keyspec_nordic_row2_10 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* keyspec_nordic_row2_11 */ "\u00E4",
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* morekeys_nordic_row2_10 */ "\u00F8",
- /* keyspec_east_slavic_row1_9 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_tablet_period */
- // U+00E6: "æ" LATIN SMALL LETTER AE
- /* morekeys_nordic_row2_11 */ "\u00E6",
- };
-
- /* Locale fr: French */
- private static final String[] TEXTS_fr = {
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E0,\u00E2,%,\u00E6,\u00E1,\u00E4,\u00E3,\u00E5,\u0101,\u00AA",
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F4,\u0153,%,\u00F6,\u00F2,\u00F3,\u00F5,\u00F8,\u014D,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,%,\u0119,\u0117,\u0113",
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00F9,\u00FB,%,\u00FC,\u00FA,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00EE,%,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
- /* morekeys_n */ null,
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,%,\u0107,\u010D",
- /* double_quotes ~ */
- null, null, null, null,
- /* ~ keyspec_currency */
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "%,\u00FF",
- /* morekeys_z ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~ keyspec_tablet_period */
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- /* keyspec_swiss_row1_11 */ "\u00E8",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* keyspec_swiss_row2_10 */ "\u00E9",
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- /* keyspec_swiss_row2_11 */ "\u00E0",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* morekeys_swiss_row1_11 */ "\u00FC",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* morekeys_swiss_row2_10 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* morekeys_swiss_row2_11 */ "\u00E4",
- };
-
- /* Locale gl_ES: Gallegan (Spain) */
- private static final String[] TEXTS_gl_ES = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- };
-
- /* Locale hi: Hindi */
- private static final String[] TEXTS_hi = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0915: "क" DEVANAGARI LETTER KA
- // U+0916: "ख" DEVANAGARI LETTER KHA
- // U+0917: "ग" DEVANAGARI LETTER GA
- /* keylabel_to_alpha */ "\u0915\u0916\u0917",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~ morekeys_cyrillic_soft_sign */
- // U+0967: "१" DEVANAGARI DIGIT ONE
- /* keyspec_symbols_1 */ "\u0967",
- // U+0968: "२" DEVANAGARI DIGIT TWO
- /* keyspec_symbols_2 */ "\u0968",
- // U+0969: "३" DEVANAGARI DIGIT THREE
- /* keyspec_symbols_3 */ "\u0969",
- // U+096A: "४" DEVANAGARI DIGIT FOUR
- /* keyspec_symbols_4 */ "\u096A",
- // U+096B: "५" DEVANAGARI DIGIT FIVE
- /* keyspec_symbols_5 */ "\u096B",
- // U+096C: "६" DEVANAGARI DIGIT SIX
- /* keyspec_symbols_6 */ "\u096C",
- // U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* keyspec_symbols_7 */ "\u096D",
- // U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* keyspec_symbols_8 */ "\u096E",
- // U+096F: "९" DEVANAGARI DIGIT NINE
- /* keyspec_symbols_9 */ "\u096F",
- // U+0966: "०" DEVANAGARI DIGIT ZERO
- /* keyspec_symbols_0 */ "\u0966",
- // Label for "switch to symbols" key.
- /* keylabel_to_symbol */ "?\u0967\u0968\u0969",
- /* additional_morekeys_symbols_1 */ "1",
- /* additional_morekeys_symbols_2 */ "2",
- /* additional_morekeys_symbols_3 */ "3",
- /* additional_morekeys_symbols_4 */ "4",
- /* additional_morekeys_symbols_5 */ "5",
- /* additional_morekeys_symbols_6 */ "6",
- /* additional_morekeys_symbols_7 */ "7",
- /* additional_morekeys_symbols_8 */ "8",
- /* additional_morekeys_symbols_9 */ "9",
- /* additional_morekeys_symbols_0 */ "0",
- /* morekeys_tablet_period */ "!autoColumnOrder!8,\\,,.,',#,),(,/,;,@,:,-,\",+,\\%,&",
- /* morekeys_nordic_row2_11 ~ */
- null, null, null,
- /* ~ keyspec_tablet_comma */
- // U+0964: "।" DEVANAGARI DANDA
- /* keyspec_period */ "\u0964",
- /* morekeys_period */ "!autoColumnOrder!9,\\,,.,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&",
- /* keyspec_tablet_period */ "\u0964",
- };
-
- /* Locale hi_ZZ: Hindi (ZZ) */
- private static final String[] TEXTS_hi_ZZ = {
- /* morekeys_a ~ */
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null,
- /* ~ morekeys_symbols_percent */
- /* label_go_key */ "Go",
- /* label_send_key */ "Send",
- /* label_next_key */ "Next",
- /* label_done_key */ "Done",
- /* label_search_key */ "Search",
- /* label_previous_key */ "Prev",
- /* label_pause_key */ "Pause",
- /* label_wait_key */ "Wait",
- };
-
- /* Locale hr: Croatian */
- private static final String[] TEXTS_hr = {
- /* morekeys_a ~ */
- null, null, null, null, null, null,
- /* ~ morekeys_i */
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- /* morekeys_c */ "\u010D,\u0107,\u00E7",
- /* double_quotes */ "!text/double_9qm_rqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- /* morekeys_s */ "\u0161,\u015B,\u00DF",
- /* single_quotes */ "!text/single_9qm_rqm",
- /* keyspec_currency */ null,
- /* morekeys_y */ null,
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- /* morekeys_z */ "\u017E,\u017A,\u017C",
- // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
- /* morekeys_d */ "\u0111",
- /* morekeys_t ~ */
- null, null, null,
- /* ~ morekeys_g */
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- };
-
- /* Locale hu: Hungarian */
- private static final String[] TEXTS_hu = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E1,\u00E0,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F3,\u00F6,\u0151,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u0171,\u00FB,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u00EC,\u012F,\u012B",
- /* morekeys_n */ null,
- /* morekeys_c */ null,
- /* double_quotes */ "!text/double_9qm_rqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_rqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_g */
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- };
-
- /* Locale hy_AM: Armenian (Armenia) */
- private static final String[] TEXTS_hy_AM = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0531: "Ա" ARMENIAN CAPITAL LETTER AYB
- // U+0532: "Բ" ARMENIAN CAPITAL LETTER BEN
- // U+0533: "Գ" ARMENIAN CAPITAL LETTER GIM
- /* keylabel_to_alpha */ "\u0531\u0532\u0533",
- /* morekeys_i ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null,
- /* ~ additional_morekeys_symbols_0 */
- /* morekeys_tablet_period */ "!text/morekeys_punctuation",
- /* morekeys_nordic_row2_11 */ null,
- // U+055E: "՞" ARMENIAN QUESTION MARK
- // U+055C: "՜" ARMENIAN EXCLAMATION MARK
- // U+055A: "՚" ARMENIAN APOSTROPHE
- // U+0559: "ՙ" ARMENIAN MODIFIER LETTER LEFT HALF RING
- // U+055D: "՝" ARMENIAN COMMA
- // U+055B: "՛" ARMENIAN EMPHASIS MARK
- // U+058A: "֊" ARMENIAN HYPHEN
- // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+055F: "՟" ARMENIAN ABBREVIATION MARK
- /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,\u055E,\u055C,.,\u055A,\u0559,?,!,\u055D,\u055B,\u058A,\u00BB,\u00AB,\u055F,;,:",
- /* keyspec_tablet_comma */ "\u055D",
- // U+0589: "։" ARMENIAN FULL STOP
- /* keyspec_period */ "\u0589",
- /* morekeys_period */ null,
- /* keyspec_tablet_period */ "\u0589",
- /* keyspec_swiss_row1_11 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~ keyspec_right_single_angle_quote */
- // U+058F: "֏" ARMENIAN DRAM SIGN
- // TODO: Enable this when we have glyph for the following letter
- // <string name="keyspec_currency">&#x058F;</string>
- //
- // U+055D: "՝" ARMENIAN COMMA
- /* keyspec_comma */ "\u055D",
- /* morekeys_tablet_comma */ null,
- /* keyhintlabel_period */ null,
- // U+055E: "՞" ARMENIAN QUESTION MARK
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* morekeys_question */ "\u055E,\u00BF",
- /* morekeys_h ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null,
- /* ~ morekeys_greater_than */
- // U+055C: "՜" ARMENIAN EXCLAMATION MARK
- // U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* morekeys_exclamation */ "\u055C,\u00A1",
- };
-
- /* Locale is: Icelandic */
- private static final String[] TEXTS_is = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E1,\u00E4,\u00E6,\u00E5,\u00E0,\u00E2,\u00E3,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00EB,\u00E8,\u00EA,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EF,\u00EE,\u00EC,\u012F,\u012B",
- /* morekeys_n */ null,
- /* morekeys_c */ null,
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- /* morekeys_z */ null,
- // U+00F0: "ð" LATIN SMALL LETTER ETH
- /* morekeys_d */ "\u00F0",
- // U+00FE: "þ" LATIN SMALL LETTER THORN
- /* morekeys_t */ "\u00FE",
- };
-
- /* Locale it: Italian */
- private static final String[] TEXTS_it = {
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101,\u00AA",
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F2,\u00F3,\u00F4,\u00F6,\u00F5,\u0153,\u00F8,\u014D,\u00BA",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0119,\u0117,\u0113",
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00F9,\u00FA,\u00FB,\u00FC,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00EC,\u00ED,\u00EE,\u00EF,\u012F,\u012B",
- /* morekeys_n ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~ keyspec_tablet_period */
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* keyspec_swiss_row1_11 */ "\u00FC",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* keyspec_swiss_row2_10 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* keyspec_swiss_row2_11 */ "\u00E4",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- /* morekeys_swiss_row1_11 */ "\u00E8",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* morekeys_swiss_row2_10 */ "\u00E9",
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- /* morekeys_swiss_row2_11 */ "\u00E0",
- };
-
- /* Locale iw: Hebrew */
- private static final String[] TEXTS_iw = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+05D0: "א" HEBREW LETTER ALEF
- // U+05D1: "ב" HEBREW LETTER BET
- // U+05D2: "ג" HEBREW LETTER GIMEL
- /* keylabel_to_alpha */ "\u05D0\u05D1\u05D2",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_rqm_9qm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_rqm_9qm",
- // U+20AA: "₪" NEW SHEQEL SIGN
- /* keyspec_currency */ "\u20AA",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_swiss_row2_11 */
- // U+2605: "★" BLACK STAR
- /* morekeys_star */ "\u2605",
- // The all letters need to be mirrored are found at
- // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- // U+2264: "≤" LESS-THAN OR EQUAL TO
- // U+2265: "≥" GREATER-THAN EQUAL TO
- // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
- // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- /* keyspec_left_parenthesis */ "(|)",
- /* keyspec_right_parenthesis */ ")|(",
- /* keyspec_left_square_bracket */ "[|]",
- /* keyspec_right_square_bracket */ "]|[",
- /* keyspec_left_curly_bracket */ "{|}",
- /* keyspec_right_curly_bracket */ "}|{",
- /* keyspec_less_than */ "<|>",
- /* keyspec_greater_than */ ">|<",
- /* keyspec_less_than_equal */ "\u2264|\u2265",
- /* keyspec_greater_than_equal */ "\u2265|\u2264",
- /* keyspec_left_double_angle_quote */ "\u00AB|\u00BB",
- /* keyspec_right_double_angle_quote */ "\u00BB|\u00AB",
- /* keyspec_left_single_angle_quote */ "\u2039|\u203A",
- /* keyspec_right_single_angle_quote */ "\u203A|\u2039",
- /* keyspec_comma ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null,
- /* ~ morekeys_currency_dollar */
- // U+00B1: "±" PLUS-MINUS SIGN
- // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
- /* morekeys_plus */ "\u00B1,\uFB29",
- };
-
- /* Locale ka_GE: Georgian (Georgia) */
- private static final String[] TEXTS_ka_GE = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+10D0: "ა" GEORGIAN LETTER AN
- // U+10D1: "ბ" GEORGIAN LETTER BAN
- // U+10D2: "გ" GEORGIAN LETTER GAN
- /* keylabel_to_alpha */ "\u10D0\u10D1\u10D2",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- };
-
- /* Locale kk: Kazakh */
- private static final String[] TEXTS_kk = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~ morekeys_k */
- // U+0451: "ё" CYRILLIC SMALL LETTER IO
- /* morekeys_cyrillic_ie */ "\u0451",
- /* keyspec_nordic_row1_11 ~ */
- null, null, null, null,
- /* ~ morekeys_nordic_row2_10 */
- // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
- /* keyspec_east_slavic_row1_9 */ "\u0449",
- // U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* keyspec_east_slavic_row2_2 */ "\u044B",
- // U+044D: "э" CYRILLIC SMALL LETTER E
- /* keyspec_east_slavic_row2_11 */ "\u044D",
- // U+0438: "и" CYRILLIC SMALL LETTER I
- /* keyspec_east_slavic_row3_5 */ "\u0438",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* morekeys_cyrillic_soft_sign */ "\u044A",
- /* keyspec_symbols_1 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_w */
- // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* morekeys_east_slavic_row2_2 */ "\u0456",
- // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
- // U+04B1: "ұ" CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
- /* morekeys_cyrillic_u */ "\u04AF,\u04B1",
- // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER
- /* morekeys_cyrillic_en */ "\u04A3",
- // U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE
- /* morekeys_cyrillic_ghe */ "\u0493",
- // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
- /* morekeys_cyrillic_o */ "\u04E9",
- /* morekeys_cyrillic_i ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null,
- /* ~ keyspec_x */
- // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA
- /* morekeys_east_slavic_row2_11 */ "\u04BB",
- // U+049B: "қ" CYRILLIC SMALL LETTER KA WITH DESCENDER
- /* morekeys_cyrillic_ka */ "\u049B",
- // U+04D9: "ә" CYRILLIC SMALL LETTER SCHWA
- /* morekeys_cyrillic_a */ "\u04D9",
- };
-
- /* Locale km_KH: Khmer (Cambodia) */
- private static final String[] TEXTS_km_KH = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+1780: "ក" KHMER LETTER KA
- // U+1781: "ខ" KHMER LETTER KHA
- // U+1782: "គ" KHMER LETTER KO
- /* keylabel_to_alpha */ "\u1780\u1781\u1782",
- /* morekeys_i ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null,
- /* ~ morekeys_cyrillic_a */
- // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL
- /* morekeys_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
- };
-
- /* Locale kn_IN: Kannada (India) */
- private static final String[] TEXTS_kn_IN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0C85: "ಅ" KANNADA LETTER A
- // U+0C86: "ಆ" KANNADA LETTER AA
- // U+0C87: "ಇ" KANNADA LETTER I
- /* keylabel_to_alpha */ "\u0C85\u0C86\u0C87",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- };
-
- /* Locale ky: Kirghiz */
- private static final String[] TEXTS_ky = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~ morekeys_k */
- // U+0451: "ё" CYRILLIC SMALL LETTER IO
- /* morekeys_cyrillic_ie */ "\u0451",
- /* keyspec_nordic_row1_11 ~ */
- null, null, null, null,
- /* ~ morekeys_nordic_row2_10 */
- // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
- /* keyspec_east_slavic_row1_9 */ "\u0449",
- // U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* keyspec_east_slavic_row2_2 */ "\u044B",
- // U+044D: "э" CYRILLIC SMALL LETTER E
- /* keyspec_east_slavic_row2_11 */ "\u044D",
- // U+0438: "и" CYRILLIC SMALL LETTER I
- /* keyspec_east_slavic_row3_5 */ "\u0438",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* morekeys_cyrillic_soft_sign */ "\u044A",
- /* keyspec_symbols_1 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_east_slavic_row2_2 */
- // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
- /* morekeys_cyrillic_u */ "\u04AF",
- // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER
- /* morekeys_cyrillic_en */ "\u04A3",
- /* morekeys_cyrillic_ghe */ null,
- // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
- /* morekeys_cyrillic_o */ "\u04E9",
- };
-
- /* Locale lo_LA: Lao (Laos) */
- private static final String[] TEXTS_lo_LA = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0E81: "ກ" LAO LETTER KO
- // U+0E82: "ຂ" LAO LETTER KHO SUNG
- // U+0E84: "ຄ" LAO LETTER KHO TAM
- /* keylabel_to_alpha */ "\u0E81\u0E82\u0E84",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20AD: "₭" KIP SIGN
- /* keyspec_currency */ "\u20AD",
- };
-
- /* Locale lt: Lithuanian */
- private static final String[] TEXTS_lt = {
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- /* morekeys_a */ "\u0105,\u00E4,\u0101,\u00E0,\u00E1,\u00E2,\u00E3,\u00E5,\u00E6",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* morekeys_o */ "\u00F6,\u00F5,\u00F2,\u00F3,\u00F4,\u0153,\u0151,\u00F8",
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- /* morekeys_e */ "\u0117,\u0119,\u0113,\u00E8,\u00E9,\u00EA,\u00EB,\u011B",
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- /* morekeys_u */ "\u016B,\u0173,\u00FC,\u016B,\u00F9,\u00FA,\u00FB,\u016F,\u0171",
- /* keylabel_to_alpha */ null,
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- /* morekeys_i */ "\u012F,\u012B,\u00EC,\u00ED,\u00EE,\u00EF,\u0131",
- // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u0146,\u00F1,\u0144",
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u00E7,\u0107",
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- /* morekeys_z */ "\u017E,\u017C,\u017A",
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- /* morekeys_d */ "\u010F",
- // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- /* morekeys_t */ "\u0163,\u0165",
- // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- /* morekeys_l */ "\u013C,\u0142,\u013A,\u013E",
- // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u0123,\u011F",
- /* single_angle_quotes */ null,
- /* double_angle_quotes */ null,
- // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- /* morekeys_r */ "\u0157,\u0159,\u0155",
- // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- /* morekeys_k */ "\u0137",
- };
-
- /* Locale lv: Latvian */
- private static final String[] TEXTS_lv = {
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- /* morekeys_a */ "\u0101,\u00E0,\u00E1,\u00E2,\u00E3,\u00E4,\u00E5,\u00E6,\u0105",
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* morekeys_o */ "\u00F2,\u00F3,\u00F4,\u00F5,\u00F6,\u0153,\u0151,\u00F8",
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- /* morekeys_e */ "\u0113,\u0117,\u00E8,\u00E9,\u00EA,\u00EB,\u0119,\u011B",
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- /* morekeys_u */ "\u016B,\u0173,\u00F9,\u00FA,\u00FB,\u00FC,\u016F,\u0171",
- /* keylabel_to_alpha */ null,
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- /* morekeys_i */ "\u012B,\u012F,\u00EC,\u00ED,\u00EE,\u00EF,\u0131",
- // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u0146,\u00F1,\u0144",
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u00E7,\u0107",
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- /* morekeys_z */ "\u017E,\u017C,\u017A",
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- /* morekeys_d */ "\u010F",
- // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- /* morekeys_t */ "\u0163,\u0165",
- // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- /* morekeys_l */ "\u013C,\u0142,\u013A,\u013E",
- // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u0123,\u011F",
- /* single_angle_quotes */ null,
- /* double_angle_quotes */ null,
- // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- /* morekeys_r */ "\u0157,\u0159,\u0155",
- // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- /* morekeys_k */ "\u0137",
- };
-
- /* Locale mk: Macedonian */
- private static final String[] TEXTS_mk = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_k */
- // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
- /* morekeys_cyrillic_ie */ "\u0450",
- /* keyspec_nordic_row1_11 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_cyrillic_o */
- // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
- /* morekeys_cyrillic_i */ "\u045D",
- // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
- /* keyspec_south_slavic_row1_6 */ "\u0455",
- // U+045C: "ќ" CYRILLIC SMALL LETTER KJE
- /* keyspec_south_slavic_row2_11 */ "\u045C",
- // U+0437: "з" CYRILLIC SMALL LETTER ZE
- /* keyspec_south_slavic_row3_1 */ "\u0437",
- // U+0453: "ѓ" CYRILLIC SMALL LETTER GJE
- /* keyspec_south_slavic_row3_8 */ "\u0453",
- };
-
- /* Locale ml_IN: Malayalam (India) */
- private static final String[] TEXTS_ml_IN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0D05: "അ" MALAYALAM LETTER A
- /* keylabel_to_alpha */ "\u0D05",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- };
-
- /* Locale mn_MN: Mongolian (Mongolia) */
- private static final String[] TEXTS_mn_MN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20AE: "₮" TUGRIK SIGN
- /* keyspec_currency */ "\u20AE",
- };
-
- /* Locale mr_IN: Marathi (India) */
- private static final String[] TEXTS_mr_IN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0915: "क" DEVANAGARI LETTER KA
- // U+0916: "ख" DEVANAGARI LETTER KHA
- // U+0917: "ग" DEVANAGARI LETTER GA
- /* keylabel_to_alpha */ "\u0915\u0916\u0917",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~ morekeys_cyrillic_soft_sign */
- // U+0967: "१" DEVANAGARI DIGIT ONE
- /* keyspec_symbols_1 */ "\u0967",
- // U+0968: "२" DEVANAGARI DIGIT TWO
- /* keyspec_symbols_2 */ "\u0968",
- // U+0969: "३" DEVANAGARI DIGIT THREE
- /* keyspec_symbols_3 */ "\u0969",
- // U+096A: "४" DEVANAGARI DIGIT FOUR
- /* keyspec_symbols_4 */ "\u096A",
- // U+096B: "५" DEVANAGARI DIGIT FIVE
- /* keyspec_symbols_5 */ "\u096B",
- // U+096C: "६" DEVANAGARI DIGIT SIX
- /* keyspec_symbols_6 */ "\u096C",
- // U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* keyspec_symbols_7 */ "\u096D",
- // U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* keyspec_symbols_8 */ "\u096E",
- // U+096F: "९" DEVANAGARI DIGIT NINE
- /* keyspec_symbols_9 */ "\u096F",
- // U+0966: "०" DEVANAGARI DIGIT ZERO
- /* keyspec_symbols_0 */ "\u0966",
- // Label for "switch to symbols" key.
- /* keylabel_to_symbol */ "?\u0967\u0968\u0969",
- /* additional_morekeys_symbols_1 */ "1",
- /* additional_morekeys_symbols_2 */ "2",
- /* additional_morekeys_symbols_3 */ "3",
- /* additional_morekeys_symbols_4 */ "4",
- /* additional_morekeys_symbols_5 */ "5",
- /* additional_morekeys_symbols_6 */ "6",
- /* additional_morekeys_symbols_7 */ "7",
- /* additional_morekeys_symbols_8 */ "8",
- /* additional_morekeys_symbols_9 */ "9",
- /* additional_morekeys_symbols_0 */ "0",
- };
-
- /* Locale nb: Norwegian Bokmål */
- private static final String[] TEXTS_nb = {
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E5,\u00E6,\u00E4,\u00E0,\u00E1,\u00E2,\u00E3,\u0101",
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F8,\u00F6,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
- /* keylabel_to_alpha ~ */
- null, null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_rqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_rqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_cyrillic_ie */
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- /* keyspec_nordic_row1_11 */ "\u00E5",
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* keyspec_nordic_row2_10 */ "\u00F8",
- // U+00E6: "æ" LATIN SMALL LETTER AE
- /* keyspec_nordic_row2_11 */ "\u00E6",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* morekeys_nordic_row2_10 */ "\u00F6",
- /* keyspec_east_slavic_row1_9 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_tablet_period */
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* morekeys_nordic_row2_11 */ "\u00E4",
- };
-
- /* Locale ne_NP: Nepali (Nepal) */
- private static final String[] TEXTS_ne_NP = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0915: "क" DEVANAGARI LETTER KA
- // U+0916: "ख" DEVANAGARI LETTER KHA
- // U+0917: "ग" DEVANAGARI LETTER GA
- /* keylabel_to_alpha */ "\u0915\u0916\u0917",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
- /* keyspec_currency */ "\u0930\u0941.",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~ morekeys_cyrillic_soft_sign */
- // U+0967: "१" DEVANAGARI DIGIT ONE
- /* keyspec_symbols_1 */ "\u0967",
- // U+0968: "२" DEVANAGARI DIGIT TWO
- /* keyspec_symbols_2 */ "\u0968",
- // U+0969: "३" DEVANAGARI DIGIT THREE
- /* keyspec_symbols_3 */ "\u0969",
- // U+096A: "४" DEVANAGARI DIGIT FOUR
- /* keyspec_symbols_4 */ "\u096A",
- // U+096B: "५" DEVANAGARI DIGIT FIVE
- /* keyspec_symbols_5 */ "\u096B",
- // U+096C: "६" DEVANAGARI DIGIT SIX
- /* keyspec_symbols_6 */ "\u096C",
- // U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* keyspec_symbols_7 */ "\u096D",
- // U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* keyspec_symbols_8 */ "\u096E",
- // U+096F: "९" DEVANAGARI DIGIT NINE
- /* keyspec_symbols_9 */ "\u096F",
- // U+0966: "०" DEVANAGARI DIGIT ZERO
- /* keyspec_symbols_0 */ "\u0966",
- // Label for "switch to symbols" key.
- /* keylabel_to_symbol */ "?\u0967\u0968\u0969",
- /* additional_morekeys_symbols_1 */ "1",
- /* additional_morekeys_symbols_2 */ "2",
- /* additional_morekeys_symbols_3 */ "3",
- /* additional_morekeys_symbols_4 */ "4",
- /* additional_morekeys_symbols_5 */ "5",
- /* additional_morekeys_symbols_6 */ "6",
- /* additional_morekeys_symbols_7 */ "7",
- /* additional_morekeys_symbols_8 */ "8",
- /* additional_morekeys_symbols_9 */ "9",
- /* additional_morekeys_symbols_0 */ "0",
- /* morekeys_tablet_period */ "!autoColumnOrder!8,.,\\,,',#,),(,/,;,@,:,-,\",+,\\%,&",
- /* morekeys_nordic_row2_11 ~ */
- null, null, null,
- /* ~ keyspec_tablet_comma */
- // U+0964: "।" DEVANAGARI DANDA
- /* keyspec_period */ "\u0964",
- /* morekeys_period */ "!autoColumnOrder!9,.,\\,,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&",
- /* keyspec_tablet_period */ "\u0964",
- };
-
- /* Locale nl: Dutch */
- private static final String[] TEXTS_nl = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E1,\u00E4,\u00E2,\u00E0,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00EB,\u00EA,\u00E8,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B,\u0133",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- /* morekeys_c */ null,
- /* double_quotes */ "!text/double_9qm_rqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_rqm",
- /* keyspec_currency */ null,
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_y */ "\u0133",
- };
-
- /* Locale pl: Polish */
- private static final String[] TEXTS_pl = {
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u0105,\u00E1,\u00E0,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u0119,\u00E8,\u00E9,\u00EA,\u00EB,\u0117,\u0113",
- /* morekeys_u ~ */
- null, null, null,
- /* ~ morekeys_i */
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u0144,\u00F1",
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u0107,\u00E7,\u010D",
- /* double_quotes */ "!text/double_9qm_rqm",
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u015B,\u00DF,\u0161",
- /* single_quotes */ "!text/single_9qm_rqm",
- /* keyspec_currency */ null,
- /* morekeys_y */ null,
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017C,\u017A,\u017E",
- /* morekeys_d */ null,
- /* morekeys_t */ null,
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "\u0142",
- };
-
- /* Locale pt: Portuguese */
- private static final String[] TEXTS_pt = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E1,\u00E3,\u00E0,\u00E2,\u00E4,\u00E5,\u00E6,\u00AA",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F3,\u00F5,\u00F4,\u00F2,\u00F6,\u0153,\u00F8,\u014D,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- /* morekeys_e */ "\u00E9,\u00EA,\u00E8,\u0119,\u0117,\u0113,\u00EB",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EE,\u00EC,\u00EF,\u012F,\u012B",
- /* morekeys_n */ null,
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u00E7,\u010D,\u0107",
- };
-
- /* Locale rm: Raeto-Romance */
- private static final String[] TEXTS_rm = {
- /* morekeys_a */ null,
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* morekeys_o */ "\u00F2,\u00F3,\u00F6,\u00F4,\u00F5,\u0153,\u00F8",
- };
-
- /* Locale ro: Romanian */
- private static final String[] TEXTS_ro = {
- // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u0103,\u00E2,\u00E3,\u00E0,\u00E1,\u00E4,\u00E6,\u00E5,\u0101",
- /* morekeys_o ~ */
- null, null, null, null,
- /* ~ keylabel_to_alpha */
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
- /* morekeys_n */ null,
- /* morekeys_c */ null,
- /* double_quotes */ "!text/double_9qm_rqm",
- // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u0219,\u00DF,\u015B,\u0161",
- /* single_quotes */ "!text/single_9qm_rqm",
- /* keyspec_currency ~ */
- null, null, null, null,
- /* ~ morekeys_d */
- // U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW
- /* morekeys_t */ "\u021B",
- };
-
- /* Locale ru: Russian */
- private static final String[] TEXTS_ru = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_k */
- // U+0451: "ё" CYRILLIC SMALL LETTER IO
- /* morekeys_cyrillic_ie */ "\u0451",
- /* keyspec_nordic_row1_11 ~ */
- null, null, null, null,
- /* ~ morekeys_nordic_row2_10 */
- // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
- /* keyspec_east_slavic_row1_9 */ "\u0449",
- // U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* keyspec_east_slavic_row2_2 */ "\u044B",
- // U+044D: "э" CYRILLIC SMALL LETTER E
- /* keyspec_east_slavic_row2_11 */ "\u044D",
- // U+0438: "и" CYRILLIC SMALL LETTER I
- /* keyspec_east_slavic_row3_5 */ "\u0438",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* morekeys_cyrillic_soft_sign */ "\u044A",
- };
-
- /* Locale si_LK: Sinhalese (Sri Lanka) */
- private static final String[] TEXTS_si_LK = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0D85: "අ" SINHALA LETTER AYANNA
- // U+0D86: "ආ" SINHALA LETTER AAYANNA
- /* keylabel_to_alpha */ "\u0D85,\u0D86",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
- /* keyspec_currency */ "\u0DBB\u0DD4",
- };
-
- /* Locale sk: Slovak */
- private static final String[] TEXTS_sk = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- /* morekeys_a */ "\u00E1,\u00E4,\u0101,\u00E0,\u00E2,\u00E3,\u00E5,\u00E6,\u0105",
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* morekeys_o */ "\u00F4,\u00F3,\u00F6,\u00F2,\u00F5,\u0153,\u0151,\u00F8",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- /* morekeys_e */ "\u00E9,\u011B,\u0113,\u0117,\u00E8,\u00EA,\u00EB,\u0119",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- /* morekeys_u */ "\u00FA,\u016F,\u00FC,\u016B,\u0173,\u00F9,\u00FB,\u0171",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- /* morekeys_i */ "\u00ED,\u012B,\u012F,\u00EC,\u00EE,\u00EF,\u0131",
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u0148,\u0146,\u00F1,\u0144",
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u00E7,\u0107",
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- /* morekeys_z */ "\u017E,\u017C,\u017A",
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- /* morekeys_d */ "\u010F",
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- /* morekeys_t */ "\u0165,\u0163",
- // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "\u013E,\u013A,\u013C,\u0142",
- // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u0123,\u011F",
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
- /* morekeys_r */ "\u0155,\u0159,\u0157",
- // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- /* morekeys_k */ "\u0137",
- };
-
- /* Locale sl: Slovenian */
- private static final String[] TEXTS_sl = {
- /* morekeys_a ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_n */
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u0107",
- /* double_quotes */ "!text/double_9qm_lqm",
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u0161",
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency */ null,
- /* morekeys_y */ null,
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017E",
- // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
- /* morekeys_d */ "\u0111",
- /* morekeys_t ~ */
- null, null, null,
- /* ~ morekeys_g */
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- };
-
- /* Locale sr: Serbian */
- private static final String[] TEXTS_sr = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // END: More keys definitions for Serbian (Cyrillic)
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- /* keyspec_currency ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_g */
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- /* morekeys_r */ null,
- /* morekeys_k */ null,
- // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
- /* morekeys_cyrillic_ie */ "\u0450",
- /* keyspec_nordic_row1_11 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_cyrillic_o */
- // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
- /* morekeys_cyrillic_i */ "\u045D",
- // TODO: Move these to sr-Latn once we can handle IETF language tag with script name specified.
- // BEGIN: More keys definitions for Serbian (Latin)
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // <string name="morekeys_s">&#x0161;,&#x00DF;,&#x015B;</string>
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // <string name="morekeys_c">&#x010D;,&#x00E7;,&#x0107;</string>
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- // <string name="morekeys_d">&#x010F;</string>
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // <string name="morekeys_z">&#x017E;,&#x017A;,&#x017C;</string>
- // END: More keys definitions for Serbian (Latin)
- // BEGIN: More keys definitions for Serbian (Cyrillic)
- // U+0437: "з" CYRILLIC SMALL LETTER ZE
- /* keyspec_south_slavic_row1_6 */ "\u0437",
- // U+045B: "ћ" CYRILLIC SMALL LETTER TSHE
- /* keyspec_south_slavic_row2_11 */ "\u045B",
- // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
- /* keyspec_south_slavic_row3_1 */ "\u0455",
- // U+0452: "ђ" CYRILLIC SMALL LETTER DJE
- /* keyspec_south_slavic_row3_8 */ "\u0452",
- };
-
- /* Locale sr_ZZ: Serbian (ZZ) */
- private static final String[] TEXTS_sr_ZZ = {
- /* morekeys_a */ null,
- /* morekeys_o */ null,
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- /* morekeys_e */ "\u00E8",
- /* morekeys_u */ null,
- /* keylabel_to_alpha */ null,
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- /* morekeys_i */ "\u00EC",
- /* morekeys_n */ null,
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- /* morekeys_c */ "\u010D,\u0107,%",
- /* double_quotes */ null,
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u0161,%",
- /* single_quotes ~ */
- null, null, null,
- /* ~ morekeys_y */
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017E,%",
- // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
- /* morekeys_d */ "\u0111,%",
- /* morekeys_t ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~ morekeys_symbols_percent */
- /* label_go_key */ "Idi",
- /* label_send_key */ "\u0160alji",
- /* label_next_key */ "Sled",
- /* label_done_key */ "Gotov",
- /* label_search_key */ "Tra\u017Ei",
- /* label_previous_key */ "Preth",
- /* label_pause_key */ "Pauza",
- /* label_wait_key */ "\u010Cekaj",
- };
-
- /* Locale sv: Swedish */
- private static final String[] TEXTS_sv = {
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- /* morekeys_a */ "\u00E4,\u00E5,\u00E6,\u00E1,\u00E0,\u00E2,\u0105,\u00E3",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F6,\u00F8,\u0153,\u00F3,\u00F2,\u00F4,\u00F5,\u014D",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FC,\u00FA,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- /* morekeys_i */ "\u00ED,\u00EC,\u00EE,\u00EF",
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- /* morekeys_n */ "\u0144,\u00F1,\u0148",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- /* double_quotes */ null,
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- /* morekeys_s */ "\u015B,\u0161,\u015F,\u00DF",
- /* single_quotes */ null,
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- /* morekeys_y */ "\u00FD,\u00FF",
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- /* morekeys_z */ "\u017A,\u017E,\u017C",
- // U+00F0: "ð" LATIN SMALL LETTER ETH
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- /* morekeys_d */ "\u00F0,\u010F",
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- // U+00FE: "þ" LATIN SMALL LETTER THORN
- /* morekeys_t */ "\u0165,\u00FE",
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "\u0142",
- /* morekeys_g */ null,
- /* single_angle_quotes */ "!text/single_raqm_laqm",
- /* double_angle_quotes */ "!text/double_raqm_laqm",
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- /* morekeys_r */ "\u0159",
- /* morekeys_k */ null,
- /* morekeys_cyrillic_ie */ null,
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- /* keyspec_nordic_row1_11 */ "\u00E5",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* keyspec_nordic_row2_10 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* keyspec_nordic_row2_11 */ "\u00E4",
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- /* morekeys_nordic_row2_10 */ "\u00F8,\u0153",
- /* keyspec_east_slavic_row1_9 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_tablet_period */
- // U+00E6: "æ" LATIN SMALL LETTER AE
- /* morekeys_nordic_row2_11 */ "\u00E6",
- };
-
- /* Locale sw: Swahili */
- private static final String[] TEXTS_sw = {
- // This is the same as English except morekeys_g.
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- /* morekeys_o */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113",
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- /* morekeys_i */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u00F1",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- /* morekeys_c */ "\u00E7",
- /* double_quotes */ null,
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- /* morekeys_s */ "\u00DF",
- /* single_quotes ~ */
- null, null, null, null, null, null, null,
- /* ~ morekeys_l */
- /* morekeys_g */ "g\'",
- };
-
- /* Locale ta_IN: Tamil (India) */
- private static final String[] TEXTS_ta_IN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0BA4: "த" TAMIL LETTER TA
- // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I
- // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA
- /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+0BF9: "௹" TAMIL RUPEE SIGN
- /* keyspec_currency */ "\u0BF9",
- };
-
- /* Locale ta_LK: Tamil (Sri Lanka) */
- private static final String[] TEXTS_ta_LK = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0BA4: "த" TAMIL LETTER TA
- // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I
- // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA
- /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
- /* keyspec_currency */ "\u0DBB\u0DD4",
- };
-
- /* Locale ta_SG: Tamil (Singapore) */
- private static final String[] TEXTS_ta_SG = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0BA4: "த" TAMIL LETTER TA
- // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I
- // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA
- /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
- };
-
- /* Locale te_IN: Telugu (India) */
- private static final String[] TEXTS_te_IN = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0C05: "అ" TELUGU LETTER A
- // U+0C06: "ఆ" TELUGU LETTER AA
- // U+0C07: "ఇ" TELUGU LETTER I
- /* keylabel_to_alpha */ "\u0C05\u0C06\u0C07",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+20B9: "₹" INDIAN RUPEE SIGN
- /* keyspec_currency */ "\u20B9",
- };
-
- /* Locale th: Thai */
- private static final String[] TEXTS_th = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0E01: "ก" THAI CHARACTER KO KAI
- // U+0E02: "ข" THAI CHARACTER KHO KHAI
- // U+0E04: "ค" THAI CHARACTER KHO KHWAI
- /* keylabel_to_alpha */ "\u0E01\u0E02\u0E04",
- /* morekeys_i ~ */
- null, null, null, null, null, null,
- /* ~ single_quotes */
- // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
- /* keyspec_currency */ "\u0E3F",
- };
-
- /* Locale tl: Tagalog */
- private static final String[] TEXTS_tl = {
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- /* morekeys_n */ "\u00F1,\u0144",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- };
-
- /* Locale tr: Turkish */
- private static final String[] TEXTS_tr = {
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- /* morekeys_a */ "\u00E2,\u00E4,\u00E1",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D",
- // U+0259: "ə" LATIN SMALL LETTER SCHWA
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* morekeys_e */ "\u0259,\u00E9",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
- /* keylabel_to_alpha */ null,
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u0148,\u00F1",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- /* double_quotes */ null,
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
- /* single_quotes */ null,
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- /* morekeys_y */ "\u00FD",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017E",
- /* morekeys_d ~ */
- null, null, null,
- /* ~ morekeys_l */
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u011F",
- };
-
- /* Locale uk: Ukrainian */
- private static final String[] TEXTS_uk = {
- /* morekeys_a ~ */
- null, null, null, null,
- /* ~ morekeys_u */
- // Label for "switch to alphabetic" key.
- // U+0410: "А" CYRILLIC CAPITAL LETTER A
- // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
- // U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* keylabel_to_alpha */ "\u0410\u0411\u0412",
- /* morekeys_i ~ */
- null, null, null,
- /* ~ morekeys_c */
- /* double_quotes */ "!text/double_9qm_lqm",
- /* morekeys_s */ null,
- /* single_quotes */ "!text/single_9qm_lqm",
- // U+20B4: "₴" HRYVNIA SIGN
- /* keyspec_currency */ "\u20B4",
- /* morekeys_y ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_nordic_row2_10 */
- // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
- /* keyspec_east_slavic_row1_9 */ "\u0449",
- // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* keyspec_east_slavic_row2_2 */ "\u0456",
- // U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE
- /* keyspec_east_slavic_row2_11 */ "\u0454",
- // U+0438: "и" CYRILLIC SMALL LETTER I
- /* keyspec_east_slavic_row3_5 */ "\u0438",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* morekeys_cyrillic_soft_sign */ "\u044A",
- /* keyspec_symbols_1 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null,
- /* ~ morekeys_w */
- // U+0457: "ї" CYRILLIC SMALL LETTER YI
- /* morekeys_east_slavic_row2_2 */ "\u0457",
- /* morekeys_cyrillic_u */ null,
- /* morekeys_cyrillic_en */ null,
- // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN
- /* morekeys_cyrillic_ghe */ "\u0491",
- };
-
- /* Locale uz_UZ: Uzbek (Uzbekistan) */
- private static final String[] TEXTS_uz_UZ = {
- // This is the same as Turkish
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- /* morekeys_a */ "\u00E2,\u00E4,\u00E1",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* morekeys_o */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D",
- // U+0259: "ə" LATIN SMALL LETTER SCHWA
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* morekeys_e */ "\u0259,\u00E9",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
- /* keylabel_to_alpha */ null,
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u0148,\u00F1",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u010D",
- /* double_quotes */ null,
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
- /* single_quotes */ null,
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- /* morekeys_y */ "\u00FD",
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017E",
- /* morekeys_d ~ */
- null, null, null,
- /* ~ morekeys_l */
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- /* morekeys_g */ "\u011F",
- };
-
- /* Locale vi: Vietnamese */
- private static final String[] TEXTS_vi = {
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+1EA3: "ả" LATIN SMALL LETTER A WITH HOOK ABOVE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+1EA1: "ạ" LATIN SMALL LETTER A WITH DOT BELOW
- // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
- // U+1EB1: "ằ" LATIN SMALL LETTER A WITH BREVE AND GRAVE
- // U+1EAF: "ắ" LATIN SMALL LETTER A WITH BREVE AND ACUTE
- // U+1EB3: "ẳ" LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE
- // U+1EB5: "ẵ" LATIN SMALL LETTER A WITH BREVE AND TILDE
- // U+1EB7: "ặ" LATIN SMALL LETTER A WITH BREVE AND DOT BELOW
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+1EA7: "ầ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE
- // U+1EA5: "ấ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
- // U+1EA9: "ẩ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
- // U+1EAB: "ẫ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE
- // U+1EAD: "ậ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW
- /* morekeys_a */ "\u00E0,\u00E1,\u1EA3,\u00E3,\u1EA1,\u0103,\u1EB1,\u1EAF,\u1EB3,\u1EB5,\u1EB7,\u00E2,\u1EA7,\u1EA5,\u1EA9,\u1EAB,\u1EAD",
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+1ECF: "ỏ" LATIN SMALL LETTER O WITH HOOK ABOVE
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+1ECD: "ọ" LATIN SMALL LETTER O WITH DOT BELOW
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+1ED3: "ồ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE
- // U+1ED1: "ố" LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE
- // U+1ED5: "ổ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
- // U+1ED7: "ỗ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE
- // U+1ED9: "ộ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW
- // U+01A1: "ơ" LATIN SMALL LETTER O WITH HORN
- // U+1EDD: "ờ" LATIN SMALL LETTER O WITH HORN AND GRAVE
- // U+1EDB: "ớ" LATIN SMALL LETTER O WITH HORN AND ACUTE
- // U+1EDF: "ở" LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE
- // U+1EE1: "ỡ" LATIN SMALL LETTER O WITH HORN AND TILDE
- // U+1EE3: "ợ" LATIN SMALL LETTER O WITH HORN AND DOT BELOW
- /* morekeys_o */ "\u00F2,\u00F3,\u1ECF,\u00F5,\u1ECD,\u00F4,\u1ED3,\u1ED1,\u1ED5,\u1ED7,\u1ED9,\u01A1,\u1EDD,\u1EDB,\u1EDF,\u1EE1,\u1EE3",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+1EBB: "ẻ" LATIN SMALL LETTER E WITH HOOK ABOVE
- // U+1EBD: "ẽ" LATIN SMALL LETTER E WITH TILDE
- // U+1EB9: "ẹ" LATIN SMALL LETTER E WITH DOT BELOW
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+1EC1: "ề" LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE
- // U+1EBF: "ế" LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE
- // U+1EC3: "ể" LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
- // U+1EC5: "ễ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE
- // U+1EC7: "ệ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
- /* morekeys_e */ "\u00E8,\u00E9,\u1EBB,\u1EBD,\u1EB9,\u00EA,\u1EC1,\u1EBF,\u1EC3,\u1EC5,\u1EC7",
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+1EE7: "ủ" LATIN SMALL LETTER U WITH HOOK ABOVE
- // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
- // U+1EE5: "ụ" LATIN SMALL LETTER U WITH DOT BELOW
- // U+01B0: "ư" LATIN SMALL LETTER U WITH HORN
- // U+1EEB: "ừ" LATIN SMALL LETTER U WITH HORN AND GRAVE
- // U+1EE9: "ứ" LATIN SMALL LETTER U WITH HORN AND ACUTE
- // U+1EED: "ử" LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE
- // U+1EEF: "ữ" LATIN SMALL LETTER U WITH HORN AND TILDE
- // U+1EF1: "ự" LATIN SMALL LETTER U WITH HORN AND DOT BELOW
- /* morekeys_u */ "\u00F9,\u00FA,\u1EE7,\u0169,\u1EE5,\u01B0,\u1EEB,\u1EE9,\u1EED,\u1EEF,\u1EF1",
- /* keylabel_to_alpha */ null,
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+1EC9: "ỉ" LATIN SMALL LETTER I WITH HOOK ABOVE
- // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
- // U+1ECB: "ị" LATIN SMALL LETTER I WITH DOT BELOW
- /* morekeys_i */ "\u00EC,\u00ED,\u1EC9,\u0129,\u1ECB",
- /* morekeys_n ~ */
- null, null, null, null, null,
- /* ~ single_quotes */
- // U+20AB: "₫" DONG SIGN
- /* keyspec_currency */ "\u20AB",
- // U+1EF3: "ỳ" LATIN SMALL LETTER Y WITH GRAVE
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+1EF7: "ỷ" LATIN SMALL LETTER Y WITH HOOK ABOVE
- // U+1EF9: "ỹ" LATIN SMALL LETTER Y WITH TILDE
- // U+1EF5: "ỵ" LATIN SMALL LETTER Y WITH DOT BELOW
- /* morekeys_y */ "\u1EF3,\u00FD,\u1EF7,\u1EF9,\u1EF5",
- /* morekeys_z */ null,
- // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
- /* morekeys_d */ "\u0111",
- };
-
- /* Locale zu: Zulu */
- private static final String[] TEXTS_zu = {
- // This is the same as English
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- /* morekeys_o */ "\u00F3,\u00F4,\u00F6,\u00F2,\u0153,\u00F8,\u014D,\u00F5",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0113",
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B",
- /* keylabel_to_alpha */ null,
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* morekeys_n */ "\u00F1",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- /* morekeys_c */ "\u00E7",
- /* double_quotes */ null,
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- /* morekeys_s */ "\u00DF",
- };
-
- /* Locale zz: Alphabet */
- private static final String[] TEXTS_zz = {
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- // U+00E6: "æ" LATIN SMALL LETTER AE
- // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
- // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- // U+00AA: "ª" FEMININE ORDINAL INDICATOR
- /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E3,\u00E4,\u00E5,\u00E6,\u0101,\u0103,\u0105,\u00AA",
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- // U+014F: "ŏ" LATIN SMALL LETTER O WITH BREVE
- // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00BA: "º" MASCULINE ORDINAL INDICATOR
- /* morekeys_o */ "\u00F2,\u00F3,\u00F4,\u00F5,\u00F6,\u00F8,\u014D,\u014F,\u0151,\u0153,\u00BA",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
- // U+0115: "ĕ" LATIN SMALL LETTER E WITH BREVE
- // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113,\u0115,\u0117,\u0119,\u011B",
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
- // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
- // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- /* morekeys_u */ "\u00F9,\u00FA,\u00FB,\u00FC,\u0169,\u016B,\u016D,\u016F,\u0171,\u0173",
- /* keylabel_to_alpha */ null,
- // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
- // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- // U+012D: "ĭ" LATIN SMALL LETTER I WITH BREVE
- // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_i */ "\u00EC,\u00ED,\u00EE,\u00EF,\u0129,\u012B,\u012D,\u012F,\u0131,\u0133",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
- // U+014B: "ŋ" LATIN SMALL LETTER ENG
- /* morekeys_n */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B",
- // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
- // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
- // U+010D: "č" LATIN SMALL LETTER C WITH CARON
- /* morekeys_c */ "\u00E7,\u0107,\u0109,\u010B,\u010D",
- /* double_quotes */ null,
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
- // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
- // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
- // U+0161: "š" LATIN SMALL LETTER S WITH CARON
- // U+017F: "ſ" LATIN SMALL LETTER LONG S
- /* morekeys_s */ "\u00DF,\u015B,\u015D,\u015F,\u0161,\u017F",
- /* single_quotes */ null,
- /* keyspec_currency */ null,
- // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
- // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- // U+0133: "ij" LATIN SMALL LIGATURE IJ
- /* morekeys_y */ "\u00FD,\u0177,\u00FF,\u0133",
- // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
- /* morekeys_z */ "\u017A,\u017C,\u017E",
- // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
- // U+00F0: "ð" LATIN SMALL LETTER ETH
- /* morekeys_d */ "\u010F,\u0111,\u00F0",
- // U+00FE: "þ" LATIN SMALL LETTER THORN
- // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
- /* morekeys_t */ "\u00FE,\u0163,\u0165,\u0167",
- // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
- // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
- /* morekeys_l */ "\u013A,\u013C,\u013E,\u0140,\u0142",
- // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
- // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
- // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
- /* morekeys_g */ "\u011D,\u011F,\u0121,\u0123",
- /* single_angle_quotes */ null,
- /* double_angle_quotes */ null,
- // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
- // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- /* morekeys_r */ "\u0155,\u0157,\u0159",
- // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- // U+0138: "ĸ" LATIN SMALL LETTER KRA
- /* morekeys_k */ "\u0137,\u0138",
- /* morekeys_cyrillic_ie ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null,
- /* ~ morekeys_question */
- // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
- /* morekeys_h */ "\u0125",
- // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
- /* morekeys_w */ "\u0175",
- /* morekeys_east_slavic_row2_2 ~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~ morekeys_v */
- // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
- /* morekeys_j */ "\u0135",
- };
-
- private static final Object[] LOCALES_AND_TEXTS = {
- // "locale", TEXT_ARRAY, /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */
- "DEFAULT", TEXTS_DEFAULT, /* 176/176 DEFAULT */
- "af" , TEXTS_af, /* 7/ 13 Afrikaans */
- "ar" , TEXTS_ar, /* 55/110 Arabic */
- "az_AZ" , TEXTS_az_AZ, /* 11/ 18 Azerbaijani (Azerbaijan) */
- "be_BY" , TEXTS_be_BY, /* 9/ 32 Belarusian (Belarus) */
- "bg" , TEXTS_bg, /* 2/ 9 Bulgarian */
- "bn_BD" , TEXTS_bn_BD, /* 2/ 12 Bengali (Bangladesh) */
- "bn_IN" , TEXTS_bn_IN, /* 2/ 12 Bengali (India) */
- "ca" , TEXTS_ca, /* 11/ 99 Catalan */
- "cs" , TEXTS_cs, /* 17/ 21 Czech */
- "da" , TEXTS_da, /* 19/ 55 Danish */
- "de" , TEXTS_de, /* 16/ 66 German */
- "el" , TEXTS_el, /* 1/ 5 Greek */
- "en" , TEXTS_en, /* 8/ 10 English */
- "eo" , TEXTS_eo, /* 26/126 Esperanto */
- "es" , TEXTS_es, /* 8/ 56 Spanish */
- "et_EE" , TEXTS_et_EE, /* 22/ 27 Estonian (Estonia) */
- "eu_ES" , TEXTS_eu_ES, /* 7/ 8 Basque (Spain) */
- "fa" , TEXTS_fa, /* 58/133 Persian */
- "fi" , TEXTS_fi, /* 10/ 55 Finnish */
- "fr" , TEXTS_fr, /* 13/ 66 French */
- "gl_ES" , TEXTS_gl_ES, /* 7/ 8 Gallegan (Spain) */
- "hi" , TEXTS_hi, /* 27/ 60 Hindi */
- "hi_ZZ" , TEXTS_hi_ZZ, /* 9/118 Hindi (ZZ) */
- "hr" , TEXTS_hr, /* 9/ 20 Croatian */
- "hu" , TEXTS_hu, /* 9/ 20 Hungarian */
- "hy_AM" , TEXTS_hy_AM, /* 9/134 Armenian (Armenia) */
- "is" , TEXTS_is, /* 10/ 16 Icelandic */
- "it" , TEXTS_it, /* 11/ 66 Italian */
- "iw" , TEXTS_iw, /* 20/131 Hebrew */
- "ka_GE" , TEXTS_ka_GE, /* 3/ 11 Georgian (Georgia) */
- "kk" , TEXTS_kk, /* 15/129 Kazakh */
- "km_KH" , TEXTS_km_KH, /* 2/130 Khmer (Cambodia) */
- "kn_IN" , TEXTS_kn_IN, /* 2/ 12 Kannada (India) */
- "ky" , TEXTS_ky, /* 10/ 92 Kirghiz */
- "lo_LA" , TEXTS_lo_LA, /* 2/ 12 Lao (Laos) */
- "lt" , TEXTS_lt, /* 18/ 22 Lithuanian */
- "lv" , TEXTS_lv, /* 18/ 22 Latvian */
- "mk" , TEXTS_mk, /* 9/ 97 Macedonian */
- "ml_IN" , TEXTS_ml_IN, /* 2/ 12 Malayalam (India) */
- "mn_MN" , TEXTS_mn_MN, /* 2/ 12 Mongolian (Mongolia) */
- "mr_IN" , TEXTS_mr_IN, /* 23/ 53 Marathi (India) */
- "nb" , TEXTS_nb, /* 11/ 55 Norwegian Bokmål */
- "ne_NP" , TEXTS_ne_NP, /* 27/ 60 Nepali (Nepal) */
- "nl" , TEXTS_nl, /* 9/ 13 Dutch */
- "pl" , TEXTS_pl, /* 10/ 17 Polish */
- "pt" , TEXTS_pt, /* 6/ 8 Portuguese */
- "rm" , TEXTS_rm, /* 1/ 2 Raeto-Romance */
- "ro" , TEXTS_ro, /* 6/ 16 Romanian */
- "ru" , TEXTS_ru, /* 9/ 32 Russian */
- "si_LK" , TEXTS_si_LK, /* 2/ 12 Sinhalese (Sri Lanka) */
- "sk" , TEXTS_sk, /* 20/ 22 Slovak */
- "sl" , TEXTS_sl, /* 8/ 20 Slovenian */
- "sr" , TEXTS_sr, /* 11/ 97 Serbian */
- "sr_ZZ" , TEXTS_sr_ZZ, /* 14/118 Serbian (ZZ) */
- "sv" , TEXTS_sv, /* 21/ 55 Swedish */
- "sw" , TEXTS_sw, /* 9/ 18 Swahili */
- "ta_IN" , TEXTS_ta_IN, /* 2/ 12 Tamil (India) */
- "ta_LK" , TEXTS_ta_LK, /* 2/ 12 Tamil (Sri Lanka) */
- "ta_SG" , TEXTS_ta_SG, /* 1/ 5 Tamil (Singapore) */
- "te_IN" , TEXTS_te_IN, /* 2/ 12 Telugu (India) */
- "th" , TEXTS_th, /* 2/ 12 Thai */
- "tl" , TEXTS_tl, /* 7/ 8 Tagalog */
- "tr" , TEXTS_tr, /* 11/ 18 Turkish */
- "uk" , TEXTS_uk, /* 11/ 91 Ukrainian */
- "uz_UZ" , TEXTS_uz_UZ, /* 11/ 18 Uzbek (Uzbekistan) */
- "vi" , TEXTS_vi, /* 8/ 15 Vietnamese */
- "zu" , TEXTS_zu, /* 8/ 10 Zulu */
- "zz" , TEXTS_zz, /* 19/120 Alphabet */
- };
-
- static {
- for (int index = 0; index < NAMES.length; index++) {
- sNameToIndexesMap.put(NAMES[index], index);
- }
-
- for (int i = 0; i < LOCALES_AND_TEXTS.length; i += 2) {
- final String locale = (String)LOCALES_AND_TEXTS[i];
- final String[] textsTable = (String[])LOCALES_AND_TEXTS[i + 1];
- sLocaleToTextsTableMap.put(locale, textsTable);
- sTextsTableToLocaleMap.put(textsTable, locale);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java b/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java
deleted file mode 100644
index d927cc362..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import android.util.Log;
-
-import java.util.Arrays;
-
-/**
- * Utilities for matrix operations. Don't instantiate objects inside this class to prevent
- * unexpected performance regressions.
- */
-@UsedForTesting
-public class MatrixUtils {
- static final String TAG = MatrixUtils.class.getSimpleName();
-
- public static class MatrixOperationFailedException extends Exception {
- private static final long serialVersionUID = 4384485606788583829L;
-
- public MatrixOperationFailedException(String msg) {
- super(msg);
- Log.d(TAG, msg);
- }
- }
-
- /**
- * A utility function to inverse matrix.
- * Find a pivot and swap the row of squareMatrix0 and squareMatrix1
- */
- private static void findPivotAndSwapRow(final int row, final float[][] squareMatrix0,
- final float[][] squareMatrix1, final int size) {
- int ip = row;
- float pivot = Math.abs(squareMatrix0[row][row]);
- for (int i = row + 1; i < size; ++i) {
- if (pivot < Math.abs(squareMatrix0[i][row])) {
- ip = i;
- pivot = Math.abs(squareMatrix0[i][row]);
- }
- }
- if (ip != row) {
- for (int j = 0; j < size; ++j) {
- final float temp0 = squareMatrix0[ip][j];
- squareMatrix0[ip][j] = squareMatrix0[row][j];
- squareMatrix0[row][j] = temp0;
- final float temp1 = squareMatrix1[ip][j];
- squareMatrix1[ip][j] = squareMatrix1[row][j];
- squareMatrix1[row][j] = temp1;
- }
- }
- }
-
- /**
- * A utility function to inverse matrix. This function calculates answer for each row by
- * sweeping method of Gauss Jordan elimination
- */
- private static void sweep(final int row, final float[][] squareMatrix0,
- final float[][] squareMatrix1, final int size) throws MatrixOperationFailedException {
- final float pivot = squareMatrix0[row][row];
- if (pivot == 0) {
- throw new MatrixOperationFailedException("Inverse failed. Invalid pivot");
- }
- for (int j = 0; j < size; ++j) {
- squareMatrix0[row][j] /= pivot;
- squareMatrix1[row][j] /= pivot;
- }
- for (int i = 0; i < size; i++) {
- final float sweepTargetValue = squareMatrix0[i][row];
- if (i != row) {
- for (int j = row; j < size; ++j) {
- squareMatrix0[i][j] -= sweepTargetValue * squareMatrix0[row][j];
- }
- for (int j = 0; j < size; ++j) {
- squareMatrix1[i][j] -= sweepTargetValue * squareMatrix1[row][j];
- }
- }
- }
- }
-
- /**
- * A function to inverse matrix.
- * The inverse matrix of squareMatrix will be output to inverseMatrix. Please notice that
- * the value of squareMatrix is modified in this function and can't be resuable.
- */
- @UsedForTesting
- public static void inverse(final float[][] squareMatrix,
- final float[][] inverseMatrix) throws MatrixOperationFailedException {
- final int size = squareMatrix.length;
- if (squareMatrix[0].length != size || inverseMatrix.length != size
- || inverseMatrix[0].length != size) {
- throw new MatrixOperationFailedException(
- "--- invalid length. column should be 2 times larger than row.");
- }
- for (int i = 0; i < size; ++i) {
- Arrays.fill(inverseMatrix[i], 0.0f);
- inverseMatrix[i][i] = 1.0f;
- }
- for (int i = 0; i < size; ++i) {
- findPivotAndSwapRow(i, squareMatrix, inverseMatrix, size);
- sweep(i, squareMatrix, inverseMatrix, size);
- }
- }
-
- /**
- * A matrix operation to multiply m0 and m1.
- */
- @UsedForTesting
- public static void multiply(final float[][] m0, final float[][] m1,
- final float[][] retval) throws MatrixOperationFailedException {
- if (m0[0].length != m1.length) {
- throw new MatrixOperationFailedException(
- "--- invalid length for multiply " + m0[0].length + ", " + m1.length);
- }
- final int m0h = m0.length;
- final int m0w = m0[0].length;
- final int m1w = m1[0].length;
- if (retval.length != m0h || retval[0].length != m1w) {
- throw new MatrixOperationFailedException(
- "--- invalid length of retval " + retval.length + ", " + retval[0].length);
- }
-
- for (int i = 0; i < m0h; i++) {
- Arrays.fill(retval[i], 0);
- for (int j = 0; j < m1w; j++) {
- for (int k = 0; k < m0w; k++) {
- retval[i][j] += m0[i][k] * m1[k][j];
- }
- }
- }
- }
-
- /**
- * A utility function to dump the specified matrix in a readable way
- */
- @UsedForTesting
- public static void dump(final String title, final float[][] a) {
- final int column = a[0].length;
- final int row = a.length;
- Log.d(TAG, "Dump matrix: " + title);
- Log.d(TAG, "/*---------------------");
- final StringBuilder sb = new StringBuilder();
- for (int i = 0; i < row; ++i) {
- sb.setLength(0);
- for (int j = 0; j < column; ++j) {
- sb.append(String.format("%4f", a[i][j])).append(' ');
- }
- Log.d(TAG, sb.toString());
- }
- Log.d(TAG, "---------------------*/");
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
deleted file mode 100644
index b254ab8d4..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.Log;
-
-/* package */ class ModifierKeyState {
- protected static final String TAG = ModifierKeyState.class.getSimpleName();
- protected static final boolean DEBUG = false;
-
- protected static final int RELEASING = 0;
- protected static final int PRESSING = 1;
- protected static final int CHORDING = 2;
-
- protected final String mName;
- protected int mState = RELEASING;
-
- public ModifierKeyState(String name) {
- mName = name;
- }
-
- public void onPress() {
- final int oldState = mState;
- mState = PRESSING;
- if (DEBUG)
- Log.d(TAG, mName + ".onPress: " + toString(oldState) + " > " + this);
- }
-
- public void onRelease() {
- final int oldState = mState;
- mState = RELEASING;
- if (DEBUG)
- Log.d(TAG, mName + ".onRelease: " + toString(oldState) + " > " + this);
- }
-
- public void onOtherKeyPressed() {
- final int oldState = mState;
- if (oldState == PRESSING)
- mState = CHORDING;
- if (DEBUG)
- Log.d(TAG, mName + ".onOtherKeyPressed: " + toString(oldState) + " > " + this);
- }
-
- public boolean isPressing() {
- return mState == PRESSING;
- }
-
- public boolean isReleasing() {
- return mState == RELEASING;
- }
-
- public boolean isChording() {
- return mState == CHORDING;
- }
-
- @Override
- public String toString() {
- return toString(mState);
- }
-
- protected String toString(int state) {
- switch (state) {
- case RELEASING: return "RELEASING";
- case PRESSING: return "PRESSING";
- case CHORDING: return "CHORDING";
- default: return "UNKNOWN";
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
deleted file mode 100644
index 0bd42fc13..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.text.TextUtils;
-import android.util.SparseIntArray;
-
-import com.android.inputmethod.compat.CharacterCompat;
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.common.CollectionUtils;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * The more key specification object. The more keys are an array of {@link MoreKeySpec}.
- *
- * The more keys specification is comma separated "key specification" each of which represents one
- * "more key".
- * The key specification might have label or string resource reference in it. These references are
- * expanded before parsing comma.
- * Special character, comma ',' backslash '\' can be escaped by '\' character.
- * Note that the '\' is also parsed by XML parser and {@link MoreKeySpec#splitKeySpecs(String)}
- * as well.
- */
-// TODO: Should extend the key specification object.
-public final class MoreKeySpec {
- public final int mCode;
- @Nullable
- public final String mLabel;
- @Nullable
- public final String mOutputText;
- public final int mIconId;
-
- public MoreKeySpec(@Nonnull final String moreKeySpec, boolean needsToUpperCase,
- @Nonnull final Locale locale) {
- if (moreKeySpec.isEmpty()) {
- throw new KeySpecParser.KeySpecParserError("Empty more key spec");
- }
- final String label = KeySpecParser.getLabel(moreKeySpec);
- mLabel = needsToUpperCase ? StringUtils.toTitleCaseOfKeyLabel(label, locale) : label;
- final int codeInSpec = KeySpecParser.getCode(moreKeySpec);
- final int code = needsToUpperCase ? StringUtils.toTitleCaseOfKeyCode(codeInSpec, locale)
- : codeInSpec;
- if (code == Constants.CODE_UNSPECIFIED) {
- // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
- // upper case representation ("SS").
- mCode = Constants.CODE_OUTPUT_TEXT;
- mOutputText = mLabel;
- } else {
- mCode = code;
- final String outputText = KeySpecParser.getOutputText(moreKeySpec);
- mOutputText = needsToUpperCase
- ? StringUtils.toTitleCaseOfKeyLabel(outputText, locale) : outputText;
- }
- mIconId = KeySpecParser.getIconId(moreKeySpec);
- }
-
- @Nonnull
- public Key buildKey(final int x, final int y, final int labelFlags,
- @Nonnull final KeyboardParams params) {
- return new Key(mLabel, mIconId, mCode, mOutputText, null /* hintLabel */, labelFlags,
- Key.BACKGROUND_TYPE_NORMAL, x, y, params.mDefaultKeyWidth, params.mDefaultRowHeight,
- params.mHorizontalGap, params.mVerticalGap);
- }
-
- @Override
- public int hashCode() {
- int hashCode = 1;
- hashCode = 31 + mCode;
- hashCode = hashCode * 31 + mIconId;
- final String label = mLabel;
- hashCode = hashCode * 31 + (label == null ? 0 : label.hashCode());
- final String outputText = mOutputText;
- hashCode = hashCode * 31 + (outputText == null ? 0 : outputText.hashCode());
- return hashCode;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) {
- return true;
- }
- if (o instanceof MoreKeySpec) {
- final MoreKeySpec other = (MoreKeySpec)o;
- return mCode == other.mCode
- && mIconId == other.mIconId
- && TextUtils.equals(mLabel, other.mLabel)
- && TextUtils.equals(mOutputText, other.mOutputText);
- }
- return false;
- }
-
- @Override
- public String toString() {
- final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
- : KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
- final String output = (mCode == Constants.CODE_OUTPUT_TEXT ? mOutputText
- : Constants.printableCode(mCode));
- if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
- return output;
- }
- return label + "|" + output;
- }
-
- public static class LettersOnBaseLayout {
- private final SparseIntArray mCodes = new SparseIntArray();
- private final HashSet<String> mTexts = new HashSet<>();
-
- public void addLetter(@Nonnull final Key key) {
- final int code = key.getCode();
- if (CharacterCompat.isAlphabetic(code)) {
- mCodes.put(code, 0);
- } else if (code == Constants.CODE_OUTPUT_TEXT) {
- mTexts.add(key.getOutputText());
- }
- }
-
- public boolean contains(@Nonnull final MoreKeySpec moreKey) {
- final int code = moreKey.mCode;
- if (CharacterCompat.isAlphabetic(code) && mCodes.indexOfKey(code) >= 0) {
- return true;
- } else if (code == Constants.CODE_OUTPUT_TEXT && mTexts.contains(moreKey.mOutputText)) {
- return true;
- }
- return false;
- }
- }
-
- @Nullable
- public static MoreKeySpec[] removeRedundantMoreKeys(@Nullable final MoreKeySpec[] moreKeys,
- @Nonnull final LettersOnBaseLayout lettersOnBaseLayout) {
- if (moreKeys == null) {
- return null;
- }
- final ArrayList<MoreKeySpec> filteredMoreKeys = new ArrayList<>();
- for (final MoreKeySpec moreKey : moreKeys) {
- if (!lettersOnBaseLayout.contains(moreKey)) {
- filteredMoreKeys.add(moreKey);
- }
- }
- final int size = filteredMoreKeys.size();
- if (size == moreKeys.length) {
- return moreKeys;
- }
- if (size == 0) {
- return null;
- }
- return filteredMoreKeys.toArray(new MoreKeySpec[size]);
- }
-
- // Constants for parsing.
- private static final char COMMA = Constants.CODE_COMMA;
- private static final char BACKSLASH = Constants.CODE_BACKSLASH;
- private static final String ADDITIONAL_MORE_KEY_MARKER =
- StringUtils.newSingleCodePointString(Constants.CODE_PERCENT);
-
- /**
- * Split the text containing multiple key specifications separated by commas into an array of
- * key specifications.
- * A key specification can contain a character escaped by the backslash character, including a
- * comma character.
- * Note that an empty key specification will be eliminated from the result array.
- *
- * @param text the text containing multiple key specifications.
- * @return an array of key specification text. Null if the specified <code>text</code> is empty
- * or has no key specifications.
- */
- @Nullable
- public static String[] splitKeySpecs(@Nullable final String text) {
- if (TextUtils.isEmpty(text)) {
- return null;
- }
- final int size = text.length();
- // Optimization for one-letter key specification.
- if (size == 1) {
- return text.charAt(0) == COMMA ? null : new String[] { text };
- }
-
- ArrayList<String> list = null;
- int start = 0;
- // The characters in question in this loop are COMMA and BACKSLASH. These characters never
- // match any high or low surrogate character. So it is OK to iterate through with char
- // index.
- for (int pos = 0; pos < size; pos++) {
- final char c = text.charAt(pos);
- if (c == COMMA) {
- // Skip empty entry.
- if (pos - start > 0) {
- if (list == null) {
- list = new ArrayList<>();
- }
- list.add(text.substring(start, pos));
- }
- // Skip comma
- start = pos + 1;
- } else if (c == BACKSLASH) {
- // Skip escape character and escaped character.
- pos++;
- }
- }
- final String remain = (size - start > 0) ? text.substring(start) : null;
- if (list == null) {
- return remain != null ? new String[] { remain } : null;
- }
- if (remain != null) {
- list.add(remain);
- }
- return list.toArray(new String[list.size()]);
- }
-
- @Nonnull
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- @Nonnull
- private static String[] filterOutEmptyString(@Nullable final String[] array) {
- if (array == null) {
- return EMPTY_STRING_ARRAY;
- }
- ArrayList<String> out = null;
- for (int i = 0; i < array.length; i++) {
- final String entry = array[i];
- if (TextUtils.isEmpty(entry)) {
- if (out == null) {
- out = CollectionUtils.arrayAsList(array, 0, i);
- }
- } else if (out != null) {
- out.add(entry);
- }
- }
- if (out == null) {
- return array;
- }
- return out.toArray(new String[out.size()]);
- }
-
- public static String[] insertAdditionalMoreKeys(@Nullable final String[] moreKeySpecs,
- @Nullable final String[] additionalMoreKeySpecs) {
- final String[] moreKeys = filterOutEmptyString(moreKeySpecs);
- final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs);
- final int moreKeysCount = moreKeys.length;
- final int additionalCount = additionalMoreKeys.length;
- ArrayList<String> out = null;
- int additionalIndex = 0;
- for (int moreKeyIndex = 0; moreKeyIndex < moreKeysCount; moreKeyIndex++) {
- final String moreKeySpec = moreKeys[moreKeyIndex];
- if (moreKeySpec.equals(ADDITIONAL_MORE_KEY_MARKER)) {
- if (additionalIndex < additionalCount) {
- // Replace '%' marker with additional more key specification.
- final String additionalMoreKey = additionalMoreKeys[additionalIndex];
- if (out != null) {
- out.add(additionalMoreKey);
- } else {
- moreKeys[moreKeyIndex] = additionalMoreKey;
- }
- additionalIndex++;
- } else {
- // Filter out excessive '%' marker.
- if (out == null) {
- out = CollectionUtils.arrayAsList(moreKeys, 0, moreKeyIndex);
- }
- }
- } else {
- if (out != null) {
- out.add(moreKeySpec);
- }
- }
- }
- if (additionalCount > 0 && additionalIndex == 0) {
- // No '%' marker is found in more keys.
- // Insert all additional more keys to the head of more keys.
- out = CollectionUtils.arrayAsList(additionalMoreKeys, additionalIndex, additionalCount);
- for (int i = 0; i < moreKeysCount; i++) {
- out.add(moreKeys[i]);
- }
- } else if (additionalIndex < additionalCount) {
- // The number of '%' markers are less than additional more keys.
- // Append remained additional more keys to the tail of more keys.
- out = CollectionUtils.arrayAsList(moreKeys, 0, moreKeysCount);
- for (int i = additionalIndex; i < additionalCount; i++) {
- out.add(additionalMoreKeys[additionalIndex]);
- }
- }
- if (out == null && moreKeysCount > 0) {
- return moreKeys;
- } else if (out != null && out.size() > 0) {
- return out.toArray(new String[out.size()]);
- } else {
- return null;
- }
- }
-
- public static int getIntValue(@Nullable final String[] moreKeys, final String key,
- final int defaultValue) {
- if (moreKeys == null) {
- return defaultValue;
- }
- final int keyLen = key.length();
- boolean foundValue = false;
- int value = defaultValue;
- for (int i = 0; i < moreKeys.length; i++) {
- final String moreKeySpec = moreKeys[i];
- if (moreKeySpec == null || !moreKeySpec.startsWith(key)) {
- continue;
- }
- moreKeys[i] = null;
- try {
- if (!foundValue) {
- value = Integer.parseInt(moreKeySpec.substring(keyLen));
- foundValue = true;
- }
- } catch (NumberFormatException e) {
- throw new RuntimeException(
- "integer should follow after " + key + ": " + moreKeySpec);
- }
- }
- return value;
- }
-
- public static boolean getBooleanValue(@Nullable final String[] moreKeys, final String key) {
- if (moreKeys == null) {
- return false;
- }
- boolean value = false;
- for (int i = 0; i < moreKeys.length; i++) {
- final String moreKeySpec = moreKeys[i];
- if (moreKeySpec == null || !moreKeySpec.equals(key)) {
- continue;
- }
- moreKeys[i] = null;
- value = true;
- }
- return value;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
deleted file mode 100644
index 8a375c620..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-
-public final class NonDistinctMultitouchHelper {
- private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName();
-
- private static final int MAIN_POINTER_TRACKER_ID = 0;
- private int mOldPointerCount = 1;
- private Key mOldKey;
- private int[] mLastCoords = CoordinateUtils.newInstance();
-
- public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
- final int pointerCount = me.getPointerCount();
- final int oldPointerCount = mOldPointerCount;
- mOldPointerCount = pointerCount;
- // Ignore continuous multi-touch events because we can't trust the coordinates
- // in multi-touch events.
- if (pointerCount > 1 && oldPointerCount > 1) {
- return;
- }
-
- // Use only main pointer tracker.
- final PointerTracker mainTracker = PointerTracker.getPointerTracker(
- MAIN_POINTER_TRACKER_ID);
- final int action = me.getActionMasked();
- final int index = me.getActionIndex();
- final long eventTime = me.getEventTime();
- final long downTime = me.getDownTime();
-
- // In single-touch.
- if (oldPointerCount == 1 && pointerCount == 1) {
- if (me.getPointerId(index) == mainTracker.mPointerId) {
- mainTracker.processMotionEvent(me, keyDetector);
- return;
- }
- // Inject a copied event.
- injectMotionEvent(action, me.getX(index), me.getY(index), downTime, eventTime,
- mainTracker, keyDetector);
- return;
- }
-
- // Single-touch to multi-touch transition.
- if (oldPointerCount == 1 && pointerCount == 2) {
- // Send an up event for the last pointer, be cause we can't trust the coordinates of
- // this multi-touch event.
- mainTracker.getLastCoordinates(mLastCoords);
- final int x = CoordinateUtils.x(mLastCoords);
- final int y = CoordinateUtils.y(mLastCoords);
- mOldKey = mainTracker.getKeyOn(x, y);
- // Inject an artifact up event for the old key.
- injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime,
- mainTracker, keyDetector);
- return;
- }
-
- // Multi-touch to single-touch transition.
- if (oldPointerCount == 2 && pointerCount == 1) {
- // Send a down event for the latest pointer if the key is different from the previous
- // key.
- final int x = (int)me.getX(index);
- final int y = (int)me.getY(index);
- final Key newKey = mainTracker.getKeyOn(x, y);
- if (mOldKey != newKey) {
- // Inject an artifact down event for the new key.
- // An artifact up event for the new key will usually be injected as a single-touch.
- injectMotionEvent(MotionEvent.ACTION_DOWN, x, y, downTime, eventTime,
- mainTracker, keyDetector);
- if (action == MotionEvent.ACTION_UP) {
- // Inject an artifact up event for the new key also.
- injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime,
- mainTracker, keyDetector);
- }
- }
- return;
- }
-
- Log.w(TAG, "Unknown touch panel behavior: pointer count is "
- + pointerCount + " (previously " + oldPointerCount + ")");
- }
-
- private static void injectMotionEvent(final int action, final float x, final float y,
- final long downTime, final long eventTime, final PointerTracker tracker,
- final KeyDetector keyDetector) {
- final MotionEvent me = MotionEvent.obtain(
- downTime, eventTime, action, x, y, 0 /* metaState */);
- try {
- tracker.processMotionEvent(me, keyDetector);
- } finally {
- me.recycle();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
deleted file mode 100644
index 556d74f4b..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-
-public final class PointerTrackerQueue {
- private static final String TAG = PointerTrackerQueue.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- public interface Element {
- public boolean isModifier();
- public boolean isInDraggingFinger();
- public void onPhantomUpEvent(long eventTime);
- public void cancelTrackingForAction();
- }
-
- private static final int INITIAL_CAPACITY = 10;
- // Note: {@link #mExpandableArrayOfActivePointers} and {@link #mArraySize} are synchronized by
- // {@link #mExpandableArrayOfActivePointers}
- private final ArrayList<Element> mExpandableArrayOfActivePointers =
- new ArrayList<>(INITIAL_CAPACITY);
- private int mArraySize = 0;
-
- public int size() {
- synchronized (mExpandableArrayOfActivePointers) {
- return mArraySize;
- }
- }
-
- public void add(final Element pointer) {
- synchronized (mExpandableArrayOfActivePointers) {
- if (DEBUG) {
- Log.d(TAG, "add: " + pointer + " " + this);
- }
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- if (arraySize < expandableArray.size()) {
- expandableArray.set(arraySize, pointer);
- } else {
- expandableArray.add(pointer);
- }
- mArraySize = arraySize + 1;
- }
- }
-
- public void remove(final Element pointer) {
- synchronized (mExpandableArrayOfActivePointers) {
- if (DEBUG) {
- Log.d(TAG, "remove: " + pointer + " " + this);
- }
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- int newIndex = 0;
- for (int index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (element == pointer) {
- if (newIndex != index) {
- Log.w(TAG, "Found duplicated element in remove: " + pointer);
- }
- continue; // Remove this element from the expandableArray.
- }
- if (newIndex != index) {
- // Shift this element toward the beginning of the expandableArray.
- expandableArray.set(newIndex, element);
- }
- newIndex++;
- }
- mArraySize = newIndex;
- }
- }
-
- public Element getOldestElement() {
- synchronized (mExpandableArrayOfActivePointers) {
- return (mArraySize == 0) ? null : mExpandableArrayOfActivePointers.get(0);
- }
- }
-
- public void releaseAllPointersOlderThan(final Element pointer, final long eventTime) {
- synchronized (mExpandableArrayOfActivePointers) {
- if (DEBUG) {
- Log.d(TAG, "releaseAllPointerOlderThan: " + pointer + " " + this);
- }
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- int newIndex, index;
- for (newIndex = index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (element == pointer) {
- break; // Stop releasing elements.
- }
- if (!element.isModifier()) {
- element.onPhantomUpEvent(eventTime);
- continue; // Remove this element from the expandableArray.
- }
- if (newIndex != index) {
- // Shift this element toward the beginning of the expandableArray.
- expandableArray.set(newIndex, element);
- }
- newIndex++;
- }
- // Shift rest of the expandableArray.
- int count = 0;
- for (; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (element == pointer) {
- count++;
- if (count > 1) {
- Log.w(TAG, "Found duplicated element in releaseAllPointersOlderThan: "
- + pointer);
- }
- }
- if (newIndex != index) {
- // Shift this element toward the beginning of the expandableArray.
- expandableArray.set(newIndex, expandableArray.get(index));
- }
- newIndex++;
- }
- mArraySize = newIndex;
- }
- }
-
- public void releaseAllPointers(final long eventTime) {
- releaseAllPointersExcept(null, eventTime);
- }
-
- public void releaseAllPointersExcept(final Element pointer, final long eventTime) {
- synchronized (mExpandableArrayOfActivePointers) {
- if (DEBUG) {
- if (pointer == null) {
- Log.d(TAG, "releaseAllPointers: " + this);
- } else {
- Log.d(TAG, "releaseAllPointerExcept: " + pointer + " " + this);
- }
- }
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- int newIndex = 0, count = 0;
- for (int index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (element == pointer) {
- count++;
- if (count > 1) {
- Log.w(TAG, "Found duplicated element in releaseAllPointersExcept: "
- + pointer);
- }
- } else {
- element.onPhantomUpEvent(eventTime);
- continue; // Remove this element from the expandableArray.
- }
- if (newIndex != index) {
- // Shift this element toward the beginning of the expandableArray.
- expandableArray.set(newIndex, element);
- }
- newIndex++;
- }
- mArraySize = newIndex;
- }
- }
-
- public boolean hasModifierKeyOlderThan(final Element pointer) {
- synchronized (mExpandableArrayOfActivePointers) {
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- for (int index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (element == pointer) {
- return false; // Stop searching modifier key.
- }
- if (element.isModifier()) {
- return true;
- }
- }
- return false;
- }
- }
-
- public boolean isAnyInDraggingFinger() {
- synchronized (mExpandableArrayOfActivePointers) {
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- for (int index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (element.isInDraggingFinger()) {
- return true;
- }
- }
- return false;
- }
- }
-
- public void cancelAllPointerTrackers() {
- synchronized (mExpandableArrayOfActivePointers) {
- if (DEBUG) {
- Log.d(TAG, "cancelAllPointerTracker: " + this);
- }
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- for (int index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- element.cancelTrackingForAction();
- }
- }
- }
-
- @Override
- public String toString() {
- synchronized (mExpandableArrayOfActivePointers) {
- final StringBuilder sb = new StringBuilder();
- final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
- final int arraySize = mArraySize;
- for (int index = 0; index < arraySize; index++) {
- final Element element = expandableArray.get(index);
- if (sb.length() > 0) {
- sb.append(" ");
- }
- sb.append(element.toString());
- }
- return "[" + sb.toString() + "]";
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java b/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java
deleted file mode 100644
index 211ef5f8b..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.RectF;
-
-public final class RoundedLine {
- private final RectF mArc1 = new RectF();
- private final RectF mArc2 = new RectF();
- private final Path mPath = new Path();
-
- private static final double RADIAN_TO_DEGREE = 180.0d / Math.PI;
- private static final double RIGHT_ANGLE = Math.PI / 2.0d;
-
- /**
- * Make a rounded line path
- *
- * @param p1x the x-coordinate of the start point.
- * @param p1y the y-coordinate of the start point.
- * @param r1 the radius at the start point
- * @param p2x the x-coordinate of the end point.
- * @param p2y the y-coordinate of the end point.
- * @param r2 the radius at the end point
- * @return an instance of {@link Path} that holds the result rounded line, or an instance of
- * {@link Path} that holds an empty path if the start and end points are equal.
- */
- public Path makePath(final float p1x, final float p1y, final float r1,
- final float p2x, final float p2y, final float r2) {
- mPath.rewind();
- final double dx = p2x - p1x;
- final double dy = p2y - p1y;
- // Distance of the points.
- final double l = Math.hypot(dx, dy);
- if (Double.compare(0.0d, l) == 0) {
- return mPath; // Return an empty path
- }
- // Angle of the line p1-p2
- final double a = Math.atan2(dy, dx);
- // Difference of trail cap radius.
- final double dr = r2 - r1;
- // Variation of angle at trail cap.
- final double ar = Math.asin(dr / l);
- // The start angle of trail cap arc at P1.
- final double aa = a - (RIGHT_ANGLE + ar);
- // The end angle of trail cap arc at P2.
- final double ab = a + (RIGHT_ANGLE + ar);
- final float cosa = (float)Math.cos(aa);
- final float sina = (float)Math.sin(aa);
- final float cosb = (float)Math.cos(ab);
- final float sinb = (float)Math.sin(ab);
- // Closing point of arc at P1.
- final float p1ax = p1x + r1 * cosa;
- final float p1ay = p1y + r1 * sina;
- // Opening point of arc at P1.
- final float p1bx = p1x + r1 * cosb;
- final float p1by = p1y + r1 * sinb;
- // Opening point of arc at P2.
- final float p2ax = p2x + r2 * cosa;
- final float p2ay = p2y + r2 * sina;
- // Closing point of arc at P2.
- final float p2bx = p2x + r2 * cosb;
- final float p2by = p2y + r2 * sinb;
- // Start angle of the trail arcs.
- final float angle = (float)(aa * RADIAN_TO_DEGREE);
- final float ar2degree = (float)(ar * 2.0d * RADIAN_TO_DEGREE);
- // Sweep angle of the trail arc at P1.
- final float a1 = -180.0f + ar2degree;
- // Sweep angle of the trail arc at P2.
- final float a2 = 180.0f + ar2degree;
- mArc1.set(p1x, p1y, p1x, p1y);
- mArc1.inset(-r1, -r1);
- mArc2.set(p2x, p2y, p2x, p2y);
- mArc2.inset(-r2, -r2);
-
- // Trail cap at P1.
- mPath.moveTo(p1x, p1y);
- mPath.arcTo(mArc1, angle, a1);
- // Trail cap at P2.
- mPath.moveTo(p2x, p2y);
- mPath.arcTo(mArc2, angle, a2);
- // Two trapezoids connecting P1 and P2.
- mPath.moveTo(p1ax, p1ay);
- mPath.lineTo(p1x, p1y);
- mPath.lineTo(p1bx, p1by);
- mPath.lineTo(p2bx, p2by);
- mPath.lineTo(p2x, p2y);
- mPath.lineTo(p2ax, p2ay);
- mPath.close();
- return mPath;
- }
-
- public void getBounds(final Rect outBounds) {
- // Reuse mArc1 as working variable
- mPath.computeBounds(mArc1, true /* unused */);
- mArc1.roundOut(outBounds);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
deleted file mode 100644
index a497b2eb3..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.util.Log;
-
-/* package */ final class ShiftKeyState extends ModifierKeyState {
- private static final int PRESSING_ON_SHIFTED = 3; // both temporary shifted & shift locked
- private static final int IGNORING = 4;
-
- public ShiftKeyState(String name) {
- super(name);
- }
-
- @Override
- public void onOtherKeyPressed() {
- int oldState = mState;
- if (oldState == PRESSING) {
- mState = CHORDING;
- } else if (oldState == PRESSING_ON_SHIFTED) {
- mState = IGNORING;
- }
- if (DEBUG)
- Log.d(TAG, mName + ".onOtherKeyPressed: " + toString(oldState) + " > " + this);
- }
-
- public void onPressOnShifted() {
- int oldState = mState;
- mState = PRESSING_ON_SHIFTED;
- if (DEBUG)
- Log.d(TAG, mName + ".onPressOnShifted: " + toString(oldState) + " > " + this);
- }
-
- public boolean isPressingOnShifted() {
- return mState == PRESSING_ON_SHIFTED;
- }
-
- public boolean isIgnoring() {
- return mState == IGNORING;
- }
-
- @Override
- public String toString() {
- return toString(mState);
- }
-
- @Override
- protected String toString(int state) {
- switch (state) {
- case PRESSING_ON_SHIFTED: return "PRESSING_ON_SHIFTED";
- case IGNORING: return "IGNORING";
- default: return super.toString(state);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
deleted file mode 100644
index 6837f0fcb..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-
-/**
- * Draw rubber band preview graphics during sliding key input.
- *
- * @attr ref android.R.styleable#MainKeyboardView_slidingKeyInputPreviewColor
- * @attr ref android.R.styleable#MainKeyboardView_slidingKeyInputPreviewWidth
- * @attr ref android.R.styleable#MainKeyboardView_slidingKeyInputPreviewBodyRatio
- * @attr ref android.R.styleable#MainKeyboardView_slidingKeyInputPreviewShadowRatio
- */
-public final class SlidingKeyInputDrawingPreview extends AbstractDrawingPreview {
- private final float mPreviewBodyRadius;
-
- private boolean mShowsSlidingKeyInputPreview;
- private final int[] mPreviewFrom = CoordinateUtils.newInstance();
- private final int[] mPreviewTo = CoordinateUtils.newInstance();
-
- // TODO: Finalize the rubber band preview implementation.
- private final RoundedLine mRoundedLine = new RoundedLine();
- private final Paint mPaint = new Paint();
-
- public SlidingKeyInputDrawingPreview(final TypedArray mainKeyboardViewAttr) {
- final int previewColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_slidingKeyInputPreviewColor, 0);
- final float previewRadius = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_slidingKeyInputPreviewWidth, 0) / 2.0f;
- final int PERCENTAGE_INT = 100;
- final float previewBodyRatio = (float)mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_slidingKeyInputPreviewBodyRatio, PERCENTAGE_INT)
- / (float)PERCENTAGE_INT;
- mPreviewBodyRadius = previewRadius * previewBodyRatio;
- final int previewShadowRatioInt = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_slidingKeyInputPreviewShadowRatio, 0);
- if (previewShadowRatioInt > 0) {
- final float previewShadowRatio = (float)previewShadowRatioInt / (float)PERCENTAGE_INT;
- final float shadowRadius = previewRadius * previewShadowRatio;
- mPaint.setShadowLayer(shadowRadius, 0.0f, 0.0f, previewColor);
- }
- mPaint.setColor(previewColor);
- }
-
- @Override
- public void onDeallocateMemory() {
- // Nothing to do here.
- }
-
- public void dismissSlidingKeyInputPreview() {
- mShowsSlidingKeyInputPreview = false;
- invalidateDrawingView();
- }
-
- /**
- * Draws the preview
- * @param canvas The canvas where the preview is drawn.
- */
- @Override
- public void drawPreview(final Canvas canvas) {
- if (!isPreviewEnabled() || !mShowsSlidingKeyInputPreview) {
- return;
- }
-
- // TODO: Finalize the rubber band preview implementation.
- final float radius = mPreviewBodyRadius;
- final Path path = mRoundedLine.makePath(
- CoordinateUtils.x(mPreviewFrom), CoordinateUtils.y(mPreviewFrom), radius,
- CoordinateUtils.x(mPreviewTo), CoordinateUtils.y(mPreviewTo), radius);
- canvas.drawPath(path, mPaint);
- }
-
- /**
- * Set the position of the preview.
- * @param tracker The new location of the preview is based on the points in PointerTracker.
- */
- @Override
- public void setPreviewPosition(final PointerTracker tracker) {
- tracker.getDownCoordinates(mPreviewFrom);
- tracker.getLastCoordinates(mPreviewTo);
- mShowsSlidingKeyInputPreview = true;
- invalidateDrawingView();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java b/java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java
deleted file mode 100644
index 10847f62d..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.internal.MatrixUtils.MatrixOperationFailedException;
-
-import android.util.Log;
-
-import java.util.Arrays;
-
-/**
- * Utilities to smooth coordinates. Currently, we calculate 3d least squares formula by using
- * Lagrangian smoothing
- */
-@UsedForTesting
-public class SmoothingUtils {
- private static final String TAG = SmoothingUtils.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private SmoothingUtils() {
- // not allowed to instantiate publicly
- }
-
- /**
- * Find a most likely 3d least squares formula for specified coordinates.
- * "retval" should be a 1x4 size matrix.
- */
- @UsedForTesting
- public static void get3DParameters(final float[] xs, final float[] ys,
- final float[][] retval) throws MatrixOperationFailedException {
- final int COEFF_COUNT = 4; // Coefficient count for 3d smoothing
- if (retval.length != COEFF_COUNT || retval[0].length != 1) {
- Log.d(TAG, "--- invalid length of 3d retval " + retval.length + ", "
- + retval[0].length);
- return;
- }
- final int N = xs.length;
- // TODO: Never isntantiate the matrix
- final float[][] m0 = new float[COEFF_COUNT][COEFF_COUNT];
- final float[][] m0Inv = new float[COEFF_COUNT][COEFF_COUNT];
- final float[][] m1 = new float[COEFF_COUNT][N];
- final float[][] m2 = new float[N][1];
-
- // m0
- for (int i = 0; i < COEFF_COUNT; ++i) {
- Arrays.fill(m0[i], 0);
- for (int j = 0; j < COEFF_COUNT; ++j) {
- final int pow = i + j;
- for (int k = 0; k < N; ++k) {
- m0[i][j] += (float) Math.pow(xs[k], pow);
- }
- }
- }
- // m0Inv
- MatrixUtils.inverse(m0, m0Inv);
- if (DEBUG) {
- MatrixUtils.dump("m0-1", m0Inv);
- }
-
- // m1
- for (int i = 0; i < COEFF_COUNT; ++i) {
- for (int j = 0; j < N; ++j) {
- m1[i][j] = (i == 0) ? 1.0f : m1[i - 1][j] * xs[j];
- }
- }
-
- // m2
- for (int i = 0; i < N; ++i) {
- m2[i][0] = ys[i];
- }
-
- final float[][] m0Invxm1 = new float[COEFF_COUNT][N];
- if (DEBUG) {
- MatrixUtils.dump("a0", m0Inv);
- MatrixUtils.dump("a1", m1);
- }
- MatrixUtils.multiply(m0Inv, m1, m0Invxm1);
- if (DEBUG) {
- MatrixUtils.dump("a2", m0Invxm1);
- MatrixUtils.dump("a3", m2);
- }
- MatrixUtils.multiply(m0Invxm1, m2, retval);
- if (DEBUG) {
- MatrixUtils.dump("result", retval);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
deleted file mode 100644
index 91f3558eb..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.os.Message;
-import android.os.SystemClock;
-import android.view.ViewConfiguration;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-
-import javax.annotation.Nonnull;
-
-public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
- implements TimerProxy {
- private static final int MSG_TYPING_STATE_EXPIRED = 0;
- private static final int MSG_REPEAT_KEY = 1;
- private static final int MSG_LONGPRESS_KEY = 2;
- private static final int MSG_LONGPRESS_SHIFT_KEY = 3;
- private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 4;
- private static final int MSG_UPDATE_BATCH_INPUT = 5;
- private static final int MSG_DISMISS_KEY_PREVIEW = 6;
- private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 7;
-
- private final int mIgnoreAltCodeKeyTimeout;
- private final int mGestureRecognitionUpdateTime;
-
- public TimerHandler(@Nonnull final DrawingProxy ownerInstance,
- final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime) {
- super(ownerInstance);
- mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout;
- mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime;
- }
-
- @Override
- public void handleMessage(final Message msg) {
- final DrawingProxy drawingProxy = getOwnerInstance();
- if (drawingProxy == null) {
- return;
- }
- switch (msg.what) {
- case MSG_TYPING_STATE_EXPIRED:
- drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN);
- break;
- case MSG_REPEAT_KEY:
- final PointerTracker tracker1 = (PointerTracker) msg.obj;
- tracker1.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
- break;
- case MSG_LONGPRESS_KEY:
- case MSG_LONGPRESS_SHIFT_KEY:
- cancelLongPressTimers();
- final PointerTracker tracker2 = (PointerTracker) msg.obj;
- tracker2.onLongPressed();
- break;
- case MSG_UPDATE_BATCH_INPUT:
- final PointerTracker tracker3 = (PointerTracker) msg.obj;
- tracker3.updateBatchInputByTimer(SystemClock.uptimeMillis());
- startUpdateBatchInputTimer(tracker3);
- break;
- case MSG_DISMISS_KEY_PREVIEW:
- drawingProxy.onKeyReleased((Key) msg.obj, false /* withAnimation */);
- break;
- case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
- drawingProxy.dismissGestureFloatingPreviewTextWithoutDelay();
- break;
- }
- }
-
- @Override
- public void startKeyRepeatTimerOf(@Nonnull final PointerTracker tracker, final int repeatCount,
- final int delay) {
- final Key key = tracker.getKey();
- if (key == null || delay == 0) {
- return;
- }
- sendMessageDelayed(
- obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay);
- }
-
- private void cancelKeyRepeatTimerOf(final PointerTracker tracker) {
- removeMessages(MSG_REPEAT_KEY, tracker);
- }
-
- public void cancelKeyRepeatTimers() {
- removeMessages(MSG_REPEAT_KEY);
- }
-
- // TODO: Suppress layout changes in key repeat mode
- public boolean isInKeyRepeat() {
- return hasMessages(MSG_REPEAT_KEY);
- }
-
- @Override
- public void startLongPressTimerOf(@Nonnull final PointerTracker tracker, final int delay) {
- final Key key = tracker.getKey();
- if (key == null) {
- return;
- }
- // Use a separate message id for long pressing shift key, because long press shift key
- // timers should be canceled when other key is pressed.
- final int messageId = (key.getCode() == Constants.CODE_SHIFT)
- ? MSG_LONGPRESS_SHIFT_KEY : MSG_LONGPRESS_KEY;
- sendMessageDelayed(obtainMessage(messageId, tracker), delay);
- }
-
- @Override
- public void cancelLongPressTimersOf(@Nonnull final PointerTracker tracker) {
- removeMessages(MSG_LONGPRESS_KEY, tracker);
- removeMessages(MSG_LONGPRESS_SHIFT_KEY, tracker);
- }
-
- @Override
- public void cancelLongPressShiftKeyTimer() {
- removeMessages(MSG_LONGPRESS_SHIFT_KEY);
- }
-
- public void cancelLongPressTimers() {
- removeMessages(MSG_LONGPRESS_KEY);
- removeMessages(MSG_LONGPRESS_SHIFT_KEY);
- }
-
- @Override
- public void startTypingStateTimer(@Nonnull final Key typedKey) {
- if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
- return;
- }
-
- final boolean isTyping = isTypingState();
- removeMessages(MSG_TYPING_STATE_EXPIRED);
- final DrawingProxy drawingProxy = getOwnerInstance();
- if (drawingProxy == null) {
- return;
- }
-
- // When user hits the space or the enter key, just cancel the while-typing timer.
- final int typedCode = typedKey.getCode();
- if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
- if (isTyping) {
- drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN);
- }
- return;
- }
-
- sendMessageDelayed(
- obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
- if (isTyping) {
- return;
- }
- drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_OUT);
- }
-
- @Override
- public boolean isTypingState() {
- return hasMessages(MSG_TYPING_STATE_EXPIRED);
- }
-
- @Override
- public void startDoubleTapShiftKeyTimer() {
- sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY),
- ViewConfiguration.getDoubleTapTimeout());
- }
-
- @Override
- public void cancelDoubleTapShiftKeyTimer() {
- removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
- }
-
- @Override
- public boolean isInDoubleTapShiftKeyTimeout() {
- return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
- }
-
- @Override
- public void cancelKeyTimersOf(@Nonnull final PointerTracker tracker) {
- cancelKeyRepeatTimerOf(tracker);
- cancelLongPressTimersOf(tracker);
- }
-
- public void cancelAllKeyTimers() {
- cancelKeyRepeatTimers();
- cancelLongPressTimers();
- }
-
- @Override
- public void startUpdateBatchInputTimer(@Nonnull final PointerTracker tracker) {
- if (mGestureRecognitionUpdateTime <= 0) {
- return;
- }
- removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
- sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
- mGestureRecognitionUpdateTime);
- }
-
- @Override
- public void cancelUpdateBatchInputTimer(@Nonnull final PointerTracker tracker) {
- removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
- }
-
- @Override
- public void cancelAllUpdateBatchInputTimers() {
- removeMessages(MSG_UPDATE_BATCH_INPUT);
- }
-
- public void postDismissKeyPreview(@Nonnull final Key key, final long delay) {
- sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay);
- }
-
- public void postDismissGestureFloatingPreviewText(final long delay) {
- sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
- }
-
- public void cancelAllMessages() {
- cancelAllKeyTimers();
- cancelAllUpdateBatchInputTimers();
- removeMessages(MSG_DISMISS_KEY_PREVIEW);
- removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java b/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java
deleted file mode 100644
index 0ce3de8d9..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.PointerTracker;
-
-import javax.annotation.Nonnull;
-
-public interface TimerProxy {
- /**
- * Start a timer to detect if a user is typing keys.
- * @param typedKey the key that is typed.
- */
- public void startTypingStateTimer(@Nonnull Key typedKey);
-
- /**
- * Check if a user is key typing.
- * @return true if a user is in typing.
- */
- public boolean isTypingState();
-
- /**
- * Start a timer to simulate repeated key presses while a user keep pressing a key.
- * @param tracker the {@link PointerTracker} that points the key to be repeated.
- * @param repeatCount the number of times that the key is repeating. Starting from 1.
- * @param delay the interval delay to the next key repeat, in millisecond.
- */
- public void startKeyRepeatTimerOf(@Nonnull PointerTracker tracker, int repeatCount, int delay);
-
- /**
- * Start a timer to detect a long pressed key.
- * If a key pointed by <code>tracker</code> is a shift key, start another timer to detect
- * long pressed shift key.
- * @param tracker the {@link PointerTracker} that starts long pressing.
- * @param delay the delay to fire the long press timer, in millisecond.
- */
- public void startLongPressTimerOf(@Nonnull PointerTracker tracker, int delay);
-
- /**
- * Cancel timers for detecting a long pressed key and a long press shift key.
- * @param tracker cancel long press timers of this {@link PointerTracker}.
- */
- public void cancelLongPressTimersOf(@Nonnull PointerTracker tracker);
-
- /**
- * Cancel a timer for detecting a long pressed shift key.
- */
- public void cancelLongPressShiftKeyTimer();
-
- /**
- * Cancel timers for detecting repeated key press, long pressed key, and long pressed shift key.
- * @param tracker the {@link PointerTracker} that starts timers to be canceled.
- */
- public void cancelKeyTimersOf(@Nonnull PointerTracker tracker);
-
- /**
- * Start a timer to detect double tapped shift key.
- */
- public void startDoubleTapShiftKeyTimer();
-
- /**
- * Cancel a timer of detecting double tapped shift key.
- */
- public void cancelDoubleTapShiftKeyTimer();
-
- /**
- * Check if a timer of detecting double tapped shift key is running.
- * @return true if detecting double tapped shift key is on going.
- */
- public boolean isInDoubleTapShiftKeyTimeout();
-
- /**
- * Start a timer to fire updating batch input while <code>tracker</code> is on hold.
- * @param tracker the {@link PointerTracker} that stops moving.
- */
- public void startUpdateBatchInputTimer(@Nonnull PointerTracker tracker);
-
- /**
- * Cancel a timer of firing updating batch input.
- * @param tracker the {@link PointerTracker} that resumes moving or ends gesture input.
- */
- public void cancelUpdateBatchInputTimer(@Nonnull PointerTracker tracker);
-
- /**
- * Cancel all timers of firing updating batch input.
- */
- public void cancelAllUpdateBatchInputTimers();
-
- public static class Adapter implements TimerProxy {
- @Override
- public void startTypingStateTimer(@Nonnull Key typedKey) {}
- @Override
- public boolean isTypingState() { return false; }
- @Override
- public void startKeyRepeatTimerOf(@Nonnull PointerTracker tracker, int repeatCount,
- int delay) {}
- @Override
- public void startLongPressTimerOf(@Nonnull PointerTracker tracker, int delay) {}
- @Override
- public void cancelLongPressTimersOf(@Nonnull PointerTracker tracker) {}
- @Override
- public void cancelLongPressShiftKeyTimer() {}
- @Override
- public void cancelKeyTimersOf(@Nonnull PointerTracker tracker) {}
- @Override
- public void startDoubleTapShiftKeyTimer() {}
- @Override
- public void cancelDoubleTapShiftKeyTimer() {}
- @Override
- public boolean isInDoubleTapShiftKeyTimeout() { return false; }
- @Override
- public void startUpdateBatchInputTimer(@Nonnull PointerTracker tracker) {}
- @Override
- public void cancelUpdateBatchInputTimer(@Nonnull PointerTracker tracker) {}
- @Override
- public void cancelAllUpdateBatchInputTimers() {}
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
deleted file mode 100644
index d8f0114e1..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.define.DebugFlags;
-
-public final class TouchPositionCorrection {
- private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
-
- private boolean mEnabled;
- private float[] mXs;
- private float[] mYs;
- private float[] mRadii;
-
- public void load(final String[] data) {
- final int dataLength = data.length;
- if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
- if (DebugFlags.DEBUG_ENABLED) {
- throw new RuntimeException(
- "the size of touch position correction data is invalid");
- }
- return;
- }
-
- final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
- mXs = new float[length];
- mYs = new float[length];
- mRadii = new float[length];
- try {
- for (int i = 0; i < dataLength; ++i) {
- final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
- final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
- final float value = Float.parseFloat(data[i]);
- if (type == 0) {
- mXs[index] = value;
- } else if (type == 1) {
- mYs[index] = value;
- } else {
- mRadii[index] = value;
- }
- }
- mEnabled = dataLength > 0;
- } catch (NumberFormatException e) {
- if (DebugFlags.DEBUG_ENABLED) {
- throw new RuntimeException(
- "the number format for touch position correction data is invalid");
- }
- mEnabled = false;
- mXs = null;
- mYs = null;
- mRadii = null;
- }
- }
-
- @UsedForTesting
- public void setEnabled(final boolean enabled) {
- mEnabled = enabled;
- }
-
- public boolean isValid() {
- return mEnabled;
- }
-
- public int getRows() {
- return mRadii.length;
- }
-
- @SuppressWarnings({ "static-method", "unused" })
- public float getX(final int row) {
- return 0.0f;
- // Touch position correction data for X coordinate is obsolete.
- // return mXs[row];
- }
-
- public float getY(final int row) {
- return mYs[row];
- }
-
- public float getRadius(final int row) {
- return mRadii[row];
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TypingTimeRecorder.java b/java/src/com/android/inputmethod/keyboard/internal/TypingTimeRecorder.java
deleted file mode 100644
index 9593f715c..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/TypingTimeRecorder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-public final class TypingTimeRecorder {
- private final int mStaticTimeThresholdAfterFastTyping; // msec
- private final int mSuppressKeyPreviewAfterBatchInputDuration;
- private long mLastTypingTime;
- private long mLastLetterTypingTime;
- private long mLastBatchInputTime;
-
- public TypingTimeRecorder(final int staticTimeThresholdAfterFastTyping,
- final int suppressKeyPreviewAfterBatchInputDuration) {
- mStaticTimeThresholdAfterFastTyping = staticTimeThresholdAfterFastTyping;
- mSuppressKeyPreviewAfterBatchInputDuration = suppressKeyPreviewAfterBatchInputDuration;
- }
-
- public boolean isInFastTyping(final long eventTime) {
- final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
- return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
- }
-
- private boolean wasLastInputTyping() {
- return mLastTypingTime >= mLastBatchInputTime;
- }
-
- public void onCodeInput(final int code, final long eventTime) {
- // Record the letter typing time when
- // 1. Letter keys are typed successively without any batch input in between.
- // 2. A letter key is typed within the threshold time since the last any key typing.
- // 3. A non-letter key is typed within the threshold time since the last letter key typing.
- if (Character.isLetter(code)) {
- if (wasLastInputTyping()
- || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
- mLastLetterTypingTime = eventTime;
- }
- } else {
- if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
- // This non-letter typing should be treated as a part of fast typing.
- mLastLetterTypingTime = eventTime;
- }
- }
- mLastTypingTime = eventTime;
- }
-
- public void onEndBatchInput(final long eventTime) {
- mLastBatchInputTime = eventTime;
- }
-
- public long getLastLetterTypingTime() {
- return mLastLetterTypingTime;
- }
-
- public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
- return !wasLastInputTyping()
- && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/UniqueKeysCache.java b/java/src/com/android/inputmethod/keyboard/internal/UniqueKeysCache.java
deleted file mode 100644
index 5b329dce4..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/UniqueKeysCache.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.keyboard.Key;
-
-import java.util.HashMap;
-
-import javax.annotation.Nonnull;
-
-public abstract class UniqueKeysCache {
- public abstract void setEnabled(boolean enabled);
- public abstract void clear();
- public abstract @Nonnull Key getUniqueKey(@Nonnull Key key);
-
- @Nonnull
- public static final UniqueKeysCache NO_CACHE = new UniqueKeysCache() {
- @Override
- public void setEnabled(boolean enabled) {}
-
- @Override
- public void clear() {}
-
- @Override
- public Key getUniqueKey(Key key) { return key; }
- };
-
- @Nonnull
- public static UniqueKeysCache newInstance() {
- return new UniqueKeysCacheImpl();
- }
-
- private static final class UniqueKeysCacheImpl extends UniqueKeysCache {
- private final HashMap<Key, Key> mCache;
-
- private boolean mEnabled;
-
- UniqueKeysCacheImpl() {
- mCache = new HashMap<>();
- }
-
- @Override
- public void setEnabled(final boolean enabled) {
- mEnabled = enabled;
- }
-
- @Override
- public void clear() {
- mCache.clear();
- }
-
- @Override
- public Key getUniqueKey(final Key key) {
- if (!mEnabled) {
- return key;
- }
- final Key existingKey = mCache.get(key);
- if (existingKey != null) {
- // Reuse the existing object that equals to "key" without adding "key" to
- // the cache.
- return existingKey;
- }
- mCache.put(key, key);
- return key;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
deleted file mode 100644
index f8d02d6ea..000000000
--- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.common.FileUtils;
-
-import java.io.File;
-
-/**
- * Immutable class to hold the address of an asset.
- * As opposed to a normal file, an asset is usually represented as a contiguous byte array in
- * the package file. Open it correctly thus requires the name of the package it is in, but
- * also the offset in the file and the length of this data. This class encapsulates these three.
- */
-public final class AssetFileAddress {
- public final String mFilename;
- public final long mOffset;
- public final long mLength;
-
- public AssetFileAddress(final String filename, final long offset, final long length) {
- mFilename = filename;
- mOffset = offset;
- mLength = length;
- }
-
- public static AssetFileAddress makeFromFile(final File file) {
- if (!file.isFile()) return null;
- return new AssetFileAddress(file.getAbsolutePath(), 0L, file.length());
- }
-
- public static AssetFileAddress makeFromFileName(final String filename) {
- if (null == filename) return null;
- return makeFromFile(new File(filename));
- }
-
- public static AssetFileAddress makeFromFileNameAndOffset(final String filename,
- final long offset, final long length) {
- if (null == filename) return null;
- final File f = new File(filename);
- if (!f.isFile()) return null;
- return new AssetFileAddress(filename, offset, length);
- }
-
- public boolean pointsToPhysicalFile() {
- return 0 == mOffset;
- }
-
- public void deleteUnderlyingFile() {
- FileUtils.deleteRecursively(new File(mFilename));
- }
-
- @Override
- public String toString() {
- return String.format("%s (offset=%d, length=%d)", mFilename, mOffset, mLength);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
deleted file mode 100644
index 5e6e4ab25..000000000
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.os.Vibrator;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-/**
- * This class gathers audio feedback and haptic feedback functions.
- *
- * It offers a consistent and simple interface that allows LatinIME to forget about the
- * complexity of settings and the like.
- */
-public final class AudioAndHapticFeedbackManager {
- private AudioManager mAudioManager;
- private Vibrator mVibrator;
-
- private SettingsValues mSettingsValues;
- private boolean mSoundOn;
-
- private static final AudioAndHapticFeedbackManager sInstance =
- new AudioAndHapticFeedbackManager();
-
- public static AudioAndHapticFeedbackManager getInstance() {
- return sInstance;
- }
-
- private AudioAndHapticFeedbackManager() {
- // Intentional empty constructor for singleton.
- }
-
- public static void init(final Context context) {
- sInstance.initInternal(context);
- }
-
- private void initInternal(final Context context) {
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- }
-
- public void performHapticAndAudioFeedback(final int code,
- final View viewToPerformHapticFeedbackOn) {
- performHapticFeedback(viewToPerformHapticFeedbackOn);
- performAudioFeedback(code);
- }
-
- public boolean hasVibrator() {
- return mVibrator != null && mVibrator.hasVibrator();
- }
-
- public void vibrate(final long milliseconds) {
- if (mVibrator == null) {
- return;
- }
- mVibrator.vibrate(milliseconds);
- }
-
- private boolean reevaluateIfSoundIsOn() {
- if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) {
- return false;
- }
- return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
- }
-
- public void performAudioFeedback(final int code) {
- // if mAudioManager is null, we can't play a sound anyway, so return
- if (mAudioManager == null) {
- return;
- }
- if (!mSoundOn) {
- return;
- }
- final int sound;
- switch (code) {
- case Constants.CODE_DELETE:
- sound = AudioManager.FX_KEYPRESS_DELETE;
- break;
- case Constants.CODE_ENTER:
- sound = AudioManager.FX_KEYPRESS_RETURN;
- break;
- case Constants.CODE_SPACE:
- sound = AudioManager.FX_KEYPRESS_SPACEBAR;
- break;
- default:
- sound = AudioManager.FX_KEYPRESS_STANDARD;
- break;
- }
- mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume);
- }
-
- public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) {
- if (!mSettingsValues.mVibrateOn) {
- return;
- }
- if (mSettingsValues.mKeypressVibrationDuration >= 0) {
- vibrate(mSettingsValues.mKeypressVibrationDuration);
- return;
- }
- // Go ahead with the system default
- if (viewToPerformHapticFeedbackOn != null) {
- viewToPerformHapticFeedbackOn.performHapticFeedback(
- HapticFeedbackConstants.KEYBOARD_TAP);
- }
- }
-
- public void onSettingsChanged(final SettingsValues settingsValues) {
- mSettingsValues = settingsValues;
- mSoundOn = reevaluateIfSoundIsOn();
- }
-
- public void onRingerModeChanged() {
- mSoundOn = reevaluateIfSoundIsOn();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/BackupAgent.java b/java/src/com/android/inputmethod/latin/BackupAgent.java
deleted file mode 100644
index b2d92b30c..000000000
--- a/java/src/com/android/inputmethod/latin/BackupAgent.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.BackupDataInput;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.SharedPreferences;
-import android.os.ParcelFileDescriptor;
-
-import com.android.inputmethod.latin.settings.LocalSettingsConstants;
-
-import java.io.IOException;
-
-/**
- * Backup/restore agent for LatinIME.
- * Currently it backs up the default shared preferences.
- */
-public final class BackupAgent extends BackupAgentHelper {
- private static final String PREF_SUFFIX = "_preferences";
-
- @Override
- public void onCreate() {
- addHelper("shared_pref", new SharedPreferencesBackupHelper(this,
- getPackageName() + PREF_SUFFIX));
- }
-
- @Override
- public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
- throws IOException {
- // Let the restore operation go through
- super.onRestore(data, appVersionCode, newState);
-
- // Remove the preferences that we don't want restored.
- final SharedPreferences.Editor prefEditor = getSharedPreferences(
- getPackageName() + PREF_SUFFIX, MODE_PRIVATE).edit();
- for (final String key : LocalSettingsConstants.PREFS_TO_SKIP_RESTORING) {
- prefEditor.remove(key);
- }
- // Flush the changes to disk.
- prefEditor.commit();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
deleted file mode 100644
index 9a3ac674e..000000000
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.FileUtils;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.WordProperty;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.JniUtils;
-import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-/**
- * Implements a static, compacted, binary dictionary of standard words.
- */
-// TODO: All methods which should be locked need to have a suffix "Locked".
-public final class BinaryDictionary extends Dictionary {
- private static final String TAG = BinaryDictionary.class.getSimpleName();
-
- // The cutoff returned by native for auto-commit confidence.
- // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h
- private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000;
-
- public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
- public static final int MAX_PREV_WORD_COUNT_FOR_N_GRAM = 3;
-
- @UsedForTesting
- public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
- @UsedForTesting
- public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
- @UsedForTesting
- public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT";
- @UsedForTesting
- public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
-
- public static final int NOT_A_VALID_TIMESTAMP = -1;
-
- // Format to get unigram flags from native side via getWordPropertyNative().
- private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 5;
- private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0;
- private static final int FORMAT_WORD_PROPERTY_IS_POSSIBLY_OFFENSIVE_INDEX = 1;
- private static final int FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX = 2;
- private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3; // DEPRECATED
- private static final int FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX = 4;
-
- // Format to get probability and historical info from native side via getWordPropertyNative().
- public static final int FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT = 4;
- public static final int FORMAT_WORD_PROPERTY_PROBABILITY_INDEX = 0;
- public static final int FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX = 1;
- public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2;
- public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3;
-
- public static final String DICT_FILE_NAME_SUFFIX_FOR_MIGRATION = ".migrate";
- public static final String DIR_NAME_SUFFIX_FOR_RECORD_MIGRATION = ".migrating";
-
- private long mNativeDict;
- private final long mDictSize;
- private final String mDictFilePath;
- private final boolean mUseFullEditDistance;
- private final boolean mIsUpdatable;
- private boolean mHasUpdated;
-
- private final SparseArray<DicTraverseSession> mDicTraverseSessions = new SparseArray<>();
-
- // TODO: There should be a way to remove used DicTraverseSession objects from
- // {@code mDicTraverseSessions}.
- private DicTraverseSession getTraverseSession(final int traverseSessionId) {
- synchronized(mDicTraverseSessions) {
- DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
- if (traverseSession == null) {
- traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize);
- mDicTraverseSessions.put(traverseSessionId, traverseSession);
- }
- return traverseSession;
- }
- }
-
- /**
- * Constructs binary dictionary using existing dictionary file.
- * @param filename the name of the file to read through native code.
- * @param offset the offset of the dictionary data within the file.
- * @param length the length of the binary data.
- * @param useFullEditDistance whether to use the full edit distance in suggestions
- * @param dictType the dictionary type, as a human-readable string
- * @param isUpdatable whether to open the dictionary file in writable mode.
- */
- public BinaryDictionary(final String filename, final long offset, final long length,
- final boolean useFullEditDistance, final Locale locale, final String dictType,
- final boolean isUpdatable) {
- super(dictType, locale);
- mDictSize = length;
- mDictFilePath = filename;
- mIsUpdatable = isUpdatable;
- mHasUpdated = false;
- mUseFullEditDistance = useFullEditDistance;
- loadDictionary(filename, offset, length, isUpdatable);
- }
-
- /**
- * Constructs binary dictionary on memory.
- * @param filename the name of the file used to flush.
- * @param useFullEditDistance whether to use the full edit distance in suggestions
- * @param dictType the dictionary type, as a human-readable string
- * @param formatVersion the format version of the dictionary
- * @param attributeMap the attributes of the dictionary
- */
- public BinaryDictionary(final String filename, final boolean useFullEditDistance,
- final Locale locale, final String dictType, final long formatVersion,
- final Map<String, String> attributeMap) {
- super(dictType, locale);
- mDictSize = 0;
- mDictFilePath = filename;
- // On memory dictionary is always updatable.
- mIsUpdatable = true;
- mHasUpdated = false;
- mUseFullEditDistance = useFullEditDistance;
- final String[] keyArray = new String[attributeMap.size()];
- final String[] valueArray = new String[attributeMap.size()];
- int index = 0;
- for (final String key : attributeMap.keySet()) {
- keyArray[index] = key;
- valueArray[index] = attributeMap.get(key);
- index++;
- }
- mNativeDict = createOnMemoryNative(formatVersion, locale.toString(), keyArray, valueArray);
- }
-
-
- static {
- JniUtils.loadNativeLibrary();
- }
-
- private static native long openNative(String sourceDir, long dictOffset, long dictSize,
- boolean isUpdatable);
- private static native long createOnMemoryNative(long formatVersion,
- String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray);
- private static native void getHeaderInfoNative(long dict, int[] outHeaderSize,
- int[] outFormatVersion, ArrayList<int[]> outAttributeKeys,
- ArrayList<int[]> outAttributeValues);
- private static native boolean flushNative(long dict, String filePath);
- private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
- private static native boolean flushWithGCNative(long dict, String filePath);
- private static native void closeNative(long dict);
- private static native int getFormatVersionNative(long dict);
- private static native int getProbabilityNative(long dict, int[] word);
- private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word);
- private static native int getNgramProbabilityNative(long dict, int[][] prevWordCodePointArrays,
- boolean[] isBeginningOfSentenceArray, int[] word);
- private static native void getWordPropertyNative(long dict, int[] word,
- boolean isBeginningOfSentence, int[] outCodePoints, boolean[] outFlags,
- int[] outProbabilityInfo, ArrayList<int[][]> outNgramPrevWordsArray,
- ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray,
- ArrayList<int[]> outNgramTargets, ArrayList<int[]> outNgramProbabilityInfo,
- ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities);
- private static native int getNextWordNative(long dict, int token, int[] outCodePoints,
- boolean[] outIsBeginningOfSentence);
- private static native void getSuggestionsNative(long dict, long proximityInfo,
- long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
- int[] pointerIds, int[] inputCodePoints, int inputSize, int[] suggestOptions,
- int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray,
- int prevWordCount, int[] outputSuggestionCount, int[] outputCodePoints,
- int[] outputScores, int[] outputIndices, int[] outputTypes,
- int[] outputAutoCommitFirstWordConfidence,
- float[] inOutWeightOfLangModelVsSpatialModel);
- private static native boolean addUnigramEntryNative(long dict, int[] word, int probability,
- int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence,
- boolean isNotAWord, boolean isPossiblyOffensive, int timestamp);
- private static native boolean removeUnigramEntryNative(long dict, int[] word);
- private static native boolean addNgramEntryNative(long dict,
- int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray,
- int[] word, int probability, int timestamp);
- private static native boolean removeNgramEntryNative(long dict,
- int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word);
- private static native boolean updateEntriesForWordWithNgramContextNative(long dict,
- int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray,
- int[] word, boolean isValidWord, int count, int timestamp);
- private static native int updateEntriesForInputEventsNative(long dict,
- WordInputEventForPersonalization[] inputEvents, int startIndex);
- private static native String getPropertyNative(long dict, String query);
- private static native boolean isCorruptedNative(long dict);
- private static native boolean migrateNative(long dict, String dictFilePath,
- long newFormatVersion);
-
- // TODO: Move native dict into session
- private void loadDictionary(final String path, final long startOffset,
- final long length, final boolean isUpdatable) {
- mHasUpdated = false;
- mNativeDict = openNative(path, startOffset, length, isUpdatable);
- }
-
- // TODO: Check isCorrupted() for main dictionaries.
- public boolean isCorrupted() {
- if (!isValidDictionary()) {
- return false;
- }
- if (!isCorruptedNative(mNativeDict)) {
- return false;
- }
- // TODO: Record the corruption.
- Log.e(TAG, "BinaryDictionary (" + mDictFilePath + ") is corrupted.");
- Log.e(TAG, "locale: " + mLocale);
- Log.e(TAG, "dict size: " + mDictSize);
- Log.e(TAG, "updatable: " + mIsUpdatable);
- return true;
- }
-
- public DictionaryHeader getHeader() throws UnsupportedFormatException {
- if (mNativeDict == 0) {
- return null;
- }
- final int[] outHeaderSize = new int[1];
- final int[] outFormatVersion = new int[1];
- final ArrayList<int[]> outAttributeKeys = new ArrayList<>();
- final ArrayList<int[]> outAttributeValues = new ArrayList<>();
- getHeaderInfoNative(mNativeDict, outHeaderSize, outFormatVersion, outAttributeKeys,
- outAttributeValues);
- final HashMap<String, String> attributes = new HashMap<>();
- for (int i = 0; i < outAttributeKeys.size(); i++) {
- final String attributeKey = StringUtils.getStringFromNullTerminatedCodePointArray(
- outAttributeKeys.get(i));
- final String attributeValue = StringUtils.getStringFromNullTerminatedCodePointArray(
- outAttributeValues.get(i));
- attributes.put(attributeKey, attributeValue);
- }
- final boolean hasHistoricalInfo = DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals(
- attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY));
- return new DictionaryHeader(outHeaderSize[0], new DictionaryOptions(attributes),
- new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo));
- }
-
- @Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
- final NgramContext ngramContext, final long proximityInfoHandle,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int sessionId, final float weightForLocale,
- final float[] inOutWeightOfLangModelVsSpatialModel) {
- if (!isValidDictionary()) {
- return null;
- }
- final DicTraverseSession session = getTraverseSession(sessionId);
- Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE);
- ngramContext.outputToArray(session.mPrevWordCodePointArrays,
- session.mIsBeginningOfSentenceArray);
- final InputPointers inputPointers = composedData.mInputPointers;
- final boolean isGesture = composedData.mIsBatchMode;
- final int inputSize;
- if (!isGesture) {
- inputSize =
- composedData.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
- session.mInputCodePoints);
- if (inputSize < 0) {
- return null;
- }
- } else {
- inputSize = inputPointers.getPointerSize();
- }
- session.mNativeSuggestOptions.setUseFullEditDistance(mUseFullEditDistance);
- session.mNativeSuggestOptions.setIsGesture(isGesture);
- session.mNativeSuggestOptions.setBlockOffensiveWords(
- settingsValuesForSuggestion.mBlockPotentiallyOffensive);
- session.mNativeSuggestOptions.setWeightForLocale(weightForLocale);
- if (inOutWeightOfLangModelVsSpatialModel != null) {
- session.mInputOutputWeightOfLangModelVsSpatialModel[0] =
- inOutWeightOfLangModelVsSpatialModel[0];
- } else {
- session.mInputOutputWeightOfLangModelVsSpatialModel[0] =
- Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL;
- }
- // TOOD: Pass multiple previous words information for n-gram.
- getSuggestionsNative(mNativeDict, proximityInfoHandle,
- getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(),
- inputPointers.getYCoordinates(), inputPointers.getTimes(),
- inputPointers.getPointerIds(), session.mInputCodePoints, inputSize,
- session.mNativeSuggestOptions.getOptions(), session.mPrevWordCodePointArrays,
- session.mIsBeginningOfSentenceArray, ngramContext.getPrevWordCount(),
- session.mOutputSuggestionCount, session.mOutputCodePoints, session.mOutputScores,
- session.mSpaceIndices, session.mOutputTypes,
- session.mOutputAutoCommitFirstWordConfidence,
- session.mInputOutputWeightOfLangModelVsSpatialModel);
- if (inOutWeightOfLangModelVsSpatialModel != null) {
- inOutWeightOfLangModelVsSpatialModel[0] =
- session.mInputOutputWeightOfLangModelVsSpatialModel[0];
- }
- final int count = session.mOutputSuggestionCount[0];
- final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
- for (int j = 0; j < count; ++j) {
- final int start = j * DICTIONARY_MAX_WORD_LENGTH;
- int len = 0;
- while (len < DICTIONARY_MAX_WORD_LENGTH
- && session.mOutputCodePoints[start + len] != 0) {
- ++len;
- }
- if (len > 0) {
- suggestions.add(new SuggestedWordInfo(
- new String(session.mOutputCodePoints, start, len),
- "" /* prevWordsContext */,
- (int)(session.mOutputScores[j] * weightForLocale),
- session.mOutputTypes[j],
- this /* sourceDict */,
- session.mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
- session.mOutputAutoCommitFirstWordConfidence[0]));
- }
- }
- return suggestions;
- }
-
- public boolean isValidDictionary() {
- return mNativeDict != 0;
- }
-
- public int getFormatVersion() {
- return getFormatVersionNative(mNativeDict);
- }
-
- @Override
- public boolean isInDictionary(final String word) {
- return getFrequency(word) != NOT_A_PROBABILITY;
- }
-
- @Override
- public int getFrequency(final String word) {
- if (TextUtils.isEmpty(word)) {
- return NOT_A_PROBABILITY;
- }
- final int[] codePoints = StringUtils.toCodePointArray(word);
- return getProbabilityNative(mNativeDict, codePoints);
- }
-
- @Override
- public int getMaxFrequencyOfExactMatches(final String word) {
- if (TextUtils.isEmpty(word)) {
- return NOT_A_PROBABILITY;
- }
- final int[] codePoints = StringUtils.toCodePointArray(word);
- return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints);
- }
-
- @UsedForTesting
- public boolean isValidNgram(final NgramContext ngramContext, final String word) {
- return getNgramProbability(ngramContext, word) != NOT_A_PROBABILITY;
- }
-
- public int getNgramProbability(final NgramContext ngramContext, final String word) {
- if (!ngramContext.isValid() || TextUtils.isEmpty(word)) {
- return NOT_A_PROBABILITY;
- }
- final int[][] prevWordCodePointArrays = new int[ngramContext.getPrevWordCount()][];
- final boolean[] isBeginningOfSentenceArray = new boolean[ngramContext.getPrevWordCount()];
- ngramContext.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
- final int[] wordCodePoints = StringUtils.toCodePointArray(word);
- return getNgramProbabilityNative(mNativeDict, prevWordCodePointArrays,
- isBeginningOfSentenceArray, wordCodePoints);
- }
-
- public WordProperty getWordProperty(final String word, final boolean isBeginningOfSentence) {
- if (word == null) {
- return null;
- }
- final int[] codePoints = StringUtils.toCodePointArray(word);
- final int[] outCodePoints = new int[DICTIONARY_MAX_WORD_LENGTH];
- final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT];
- final int[] outProbabilityInfo =
- new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT];
- final ArrayList<int[][]> outNgramPrevWordsArray = new ArrayList<>();
- final ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray =
- new ArrayList<>();
- final ArrayList<int[]> outNgramTargets = new ArrayList<>();
- final ArrayList<int[]> outNgramProbabilityInfo = new ArrayList<>();
- final ArrayList<int[]> outShortcutTargets = new ArrayList<>();
- final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>();
- getWordPropertyNative(mNativeDict, codePoints, isBeginningOfSentence, outCodePoints,
- outFlags, outProbabilityInfo, outNgramPrevWordsArray,
- outNgramPrevWordIsBeginningOfSentenceArray, outNgramTargets,
- outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities);
- return new WordProperty(codePoints,
- outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX],
- outFlags[FORMAT_WORD_PROPERTY_IS_POSSIBLY_OFFENSIVE_INDEX],
- outFlags[FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX],
- outFlags[FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX], outProbabilityInfo,
- outNgramPrevWordsArray, outNgramPrevWordIsBeginningOfSentenceArray,
- outNgramTargets, outNgramProbabilityInfo);
- }
-
- public static class GetNextWordPropertyResult {
- public WordProperty mWordProperty;
- public int mNextToken;
-
- public GetNextWordPropertyResult(final WordProperty wordProperty, final int nextToken) {
- mWordProperty = wordProperty;
- mNextToken = nextToken;
- }
- }
-
- /**
- * Method to iterate all words in the dictionary for makedict.
- * If token is 0, this method newly starts iterating the dictionary.
- */
- public GetNextWordPropertyResult getNextWordProperty(final int token) {
- final int[] codePoints = new int[DICTIONARY_MAX_WORD_LENGTH];
- final boolean[] isBeginningOfSentence = new boolean[1];
- final int nextToken = getNextWordNative(mNativeDict, token, codePoints,
- isBeginningOfSentence);
- final String word = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
- return new GetNextWordPropertyResult(
- getWordProperty(word, isBeginningOfSentence[0]), nextToken);
- }
-
- // Add a unigram entry to binary dictionary with unigram attributes in native code.
- public boolean addUnigramEntry(
- final String word, final int probability, final boolean isBeginningOfSentence,
- final boolean isNotAWord, final boolean isPossiblyOffensive, final int timestamp) {
- if (word == null || (word.isEmpty() && !isBeginningOfSentence)) {
- return false;
- }
- final int[] codePoints = StringUtils.toCodePointArray(word);
- if (!addUnigramEntryNative(mNativeDict, codePoints, probability,
- null /* shortcutTargetCodePoints */, 0 /* shortcutProbability */,
- isBeginningOfSentence, isNotAWord, isPossiblyOffensive, timestamp)) {
- return false;
- }
- mHasUpdated = true;
- return true;
- }
-
- // Remove a unigram entry from the binary dictionary in native code.
- public boolean removeUnigramEntry(final String word) {
- if (TextUtils.isEmpty(word)) {
- return false;
- }
- final int[] codePoints = StringUtils.toCodePointArray(word);
- if (!removeUnigramEntryNative(mNativeDict, codePoints)) {
- return false;
- }
- mHasUpdated = true;
- return true;
- }
-
- // Add an n-gram entry to the binary dictionary with timestamp in native code.
- public boolean addNgramEntry(final NgramContext ngramContext, final String word,
- final int probability, final int timestamp) {
- if (!ngramContext.isValid() || TextUtils.isEmpty(word)) {
- return false;
- }
- final int[][] prevWordCodePointArrays = new int[ngramContext.getPrevWordCount()][];
- final boolean[] isBeginningOfSentenceArray = new boolean[ngramContext.getPrevWordCount()];
- ngramContext.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
- final int[] wordCodePoints = StringUtils.toCodePointArray(word);
- if (!addNgramEntryNative(mNativeDict, prevWordCodePointArrays,
- isBeginningOfSentenceArray, wordCodePoints, probability, timestamp)) {
- return false;
- }
- mHasUpdated = true;
- return true;
- }
-
- // Update entries for the word occurrence with the ngramContext.
- public boolean updateEntriesForWordWithNgramContext(@Nonnull final NgramContext ngramContext,
- final String word, final boolean isValidWord, final int count, final int timestamp) {
- if (TextUtils.isEmpty(word)) {
- return false;
- }
- final int[][] prevWordCodePointArrays = new int[ngramContext.getPrevWordCount()][];
- final boolean[] isBeginningOfSentenceArray = new boolean[ngramContext.getPrevWordCount()];
- ngramContext.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
- final int[] wordCodePoints = StringUtils.toCodePointArray(word);
- if (!updateEntriesForWordWithNgramContextNative(mNativeDict, prevWordCodePointArrays,
- isBeginningOfSentenceArray, wordCodePoints, isValidWord, count, timestamp)) {
- return false;
- }
- mHasUpdated = true;
- return true;
- }
-
- @UsedForTesting
- public void updateEntriesForInputEvents(final WordInputEventForPersonalization[] inputEvents) {
- if (!isValidDictionary()) {
- return;
- }
- int processedEventCount = 0;
- while (processedEventCount < inputEvents.length) {
- if (needsToRunGC(true /* mindsBlockByGC */)) {
- flushWithGC();
- }
- processedEventCount = updateEntriesForInputEventsNative(mNativeDict, inputEvents,
- processedEventCount);
- mHasUpdated = true;
- if (processedEventCount <= 0) {
- return;
- }
- }
- }
-
- private void reopen() {
- close();
- final File dictFile = new File(mDictFilePath);
- // WARNING: Because we pass 0 as the offset and file.length() as the length, this can
- // only be called for actual files. Right now it's only called by the flush() family of
- // functions, which require an updatable dictionary, so it's okay. But beware.
- loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
- dictFile.length(), mIsUpdatable);
- }
-
- // Flush to dict file if the dictionary has been updated.
- public boolean flush() {
- if (!isValidDictionary()) {
- return false;
- }
- if (mHasUpdated) {
- if (!flushNative(mNativeDict, mDictFilePath)) {
- return false;
- }
- reopen();
- }
- return true;
- }
-
- // Run GC and flush to dict file if the dictionary has been updated.
- public boolean flushWithGCIfHasUpdated() {
- if (mHasUpdated) {
- return flushWithGC();
- }
- return true;
- }
-
- // Run GC and flush to dict file.
- public boolean flushWithGC() {
- if (!isValidDictionary()) {
- return false;
- }
- if (!flushWithGCNative(mNativeDict, mDictFilePath)) {
- return false;
- }
- reopen();
- return true;
- }
-
- /**
- * Checks whether GC is needed to run or not.
- * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about
- * the blocking in some situations such as in idle time or just before closing.
- * @return whether GC is needed to run or not.
- */
- public boolean needsToRunGC(final boolean mindsBlockByGC) {
- if (!isValidDictionary()) {
- return false;
- }
- return needsToRunGCNative(mNativeDict, mindsBlockByGC);
- }
-
- public boolean migrateTo(final int newFormatVersion) {
- if (!isValidDictionary()) {
- return false;
- }
- final File isMigratingDir =
- new File(mDictFilePath + DIR_NAME_SUFFIX_FOR_RECORD_MIGRATION);
- if (isMigratingDir.exists()) {
- isMigratingDir.delete();
- Log.e(TAG, "Previous migration attempt failed probably due to a crash. "
- + "Giving up using the old dictionary (" + mDictFilePath + ").");
- return false;
- }
- if (!isMigratingDir.mkdir()) {
- Log.e(TAG, "Cannot create a dir (" + isMigratingDir.getAbsolutePath()
- + ") to record migration.");
- return false;
- }
- try {
- final String tmpDictFilePath = mDictFilePath + DICT_FILE_NAME_SUFFIX_FOR_MIGRATION;
- if (!migrateNative(mNativeDict, tmpDictFilePath, newFormatVersion)) {
- return false;
- }
- close();
- final File dictFile = new File(mDictFilePath);
- final File tmpDictFile = new File(tmpDictFilePath);
- if (!FileUtils.deleteRecursively(dictFile)) {
- return false;
- }
- if (!BinaryDictionaryUtils.renameDict(tmpDictFile, dictFile)) {
- return false;
- }
- loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
- dictFile.length(), mIsUpdatable);
- return true;
- } finally {
- isMigratingDir.delete();
- }
- }
-
- @UsedForTesting
- public String getPropertyForGettingStats(final String query) {
- if (!isValidDictionary()) {
- return "";
- }
- return getPropertyNative(mNativeDict, query);
- }
-
- @Override
- public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
- return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT;
- }
-
- @Override
- public void close() {
- synchronized (mDicTraverseSessions) {
- final int sessionsSize = mDicTraverseSessions.size();
- for (int index = 0; index < sessionsSize; ++index) {
- final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
- if (traverseSession != null) {
- traverseSession.close();
- }
- }
- mDicTraverseSessions.clear();
- }
- closeInternalLocked();
- }
-
- private synchronized void closeInternalLocked() {
- if (mNativeDict != 0) {
- closeNative(mNativeDict);
- mNativeDict = 0;
- }
- }
-
- // TODO: Manage BinaryDictionary instances without using WeakReference or something.
- @Override
- protected void finalize() throws Throwable {
- try {
- closeInternalLocked();
- } finally {
- super.finalize();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
deleted file mode 100644
index 648610c86..000000000
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
-import com.android.inputmethod.dictionarypack.MD5Calculator;
-import com.android.inputmethod.dictionarypack.UpdateHandler;
-import com.android.inputmethod.latin.common.FileUtils;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-import com.android.inputmethod.latin.utils.DictionaryInfoUtils.DictionaryInfo;
-import com.android.inputmethod.latin.utils.FileTransforms;
-import com.android.inputmethod.latin.utils.MetadataFileUriGetter;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Group class for static methods to help with creation and getting of the binary dictionary
- * file from the dictionary provider
- */
-public final class BinaryDictionaryFileDumper {
- private static final String TAG = BinaryDictionaryFileDumper.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- /**
- * The size of the temporary buffer to copy files.
- */
- private static final int FILE_READ_BUFFER_SIZE = 8192;
- // TODO: make the following data common with the native code
- private static final byte[] MAGIC_NUMBER_VERSION_1 =
- new byte[] { (byte)0x78, (byte)0xB1, (byte)0x00, (byte)0x00 };
- private static final byte[] MAGIC_NUMBER_VERSION_2 =
- new byte[] { (byte)0x9B, (byte)0xC1, (byte)0x3A, (byte)0xFE };
-
- private static final boolean SHOULD_VERIFY_MAGIC_NUMBER =
- DecoderSpecificConstants.SHOULD_VERIFY_MAGIC_NUMBER;
- private static final boolean SHOULD_VERIFY_CHECKSUM =
- DecoderSpecificConstants.SHOULD_VERIFY_CHECKSUM;
-
- private static final String DICTIONARY_PROJECTION[] = { "id" };
-
- private static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt";
- private static final String QUERY_PARAMETER_TRUE = "true";
- private static final String QUERY_PARAMETER_DELETE_RESULT = "result";
- private static final String QUERY_PARAMETER_SUCCESS = "success";
- private static final String QUERY_PARAMETER_FAILURE = "failure";
-
- // Using protocol version 2 to communicate with the dictionary pack
- private static final String QUERY_PARAMETER_PROTOCOL = "protocol";
- private static final String QUERY_PARAMETER_PROTOCOL_VALUE = "2";
-
- // The path fragment to append after the client ID for dictionary info requests.
- private static final String QUERY_PATH_DICT_INFO = "dict";
- // The path fragment to append after the client ID for dictionary datafile requests.
- private static final String QUERY_PATH_DATAFILE = "datafile";
- // The path fragment to append after the client ID for updating the metadata URI.
- private static final String QUERY_PATH_METADATA = "metadata";
- private static final String INSERT_METADATA_CLIENT_ID_COLUMN = "clientid";
- private static final String INSERT_METADATA_METADATA_URI_COLUMN = "uri";
- private static final String INSERT_METADATA_METADATA_ADDITIONAL_ID_COLUMN = "additionalid";
-
- // Prevents this class to be accidentally instantiated.
- private BinaryDictionaryFileDumper() {
- }
-
- /**
- * Returns a URI builder pointing to the dictionary pack.
- *
- * This creates a URI builder able to build a URI pointing to the dictionary
- * pack content provider for a specific dictionary id.
- */
- public static Uri.Builder getProviderUriBuilder(final String path) {
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(DictionaryPackConstants.AUTHORITY).appendPath(path);
- }
-
- /**
- * Gets the content URI builder for a specified type.
- *
- * Supported types include QUERY_PATH_DICT_INFO, which takes the locale as
- * the extraPath argument, and QUERY_PATH_DATAFILE, which needs a wordlist ID
- * as the extraPath argument.
- *
- * @param clientId the clientId to use
- * @param contentProviderClient the instance of content provider client
- * @param queryPathType the path element encoding the type
- * @param extraPath optional extra argument for this type (typically word list id)
- * @return a builder that can build the URI for the best supported protocol version
- * @throws RemoteException if the client can't be contacted
- */
- private static Uri.Builder getContentUriBuilderForType(final String clientId,
- final ContentProviderClient contentProviderClient, final String queryPathType,
- final String extraPath) throws RemoteException {
- // Check whether protocol v2 is supported by building a v2 URI and calling getType()
- // on it. If this returns null, v2 is not supported.
- final Uri.Builder uriV2Builder = getProviderUriBuilder(clientId);
- uriV2Builder.appendPath(queryPathType);
- uriV2Builder.appendPath(extraPath);
- uriV2Builder.appendQueryParameter(QUERY_PARAMETER_PROTOCOL,
- QUERY_PARAMETER_PROTOCOL_VALUE);
- if (null != contentProviderClient.getType(uriV2Builder.build())) return uriV2Builder;
- // Protocol v2 is not supported, so create and return the protocol v1 uri.
- return getProviderUriBuilder(extraPath);
- }
-
- /**
- * Queries a content provider for the list of word lists for a specific locale
- * available to copy into Latin IME.
- */
- private static List<WordListInfo> getWordListWordListInfos(final Locale locale,
- final Context context, final boolean hasDefaultWordList) {
- final String clientId = context.getString(R.string.dictionary_pack_client_id);
- final ContentProviderClient client = context.getContentResolver().
- acquireContentProviderClient(getProviderUriBuilder("").build());
- if (null == client) return Collections.<WordListInfo>emptyList();
- Cursor cursor = null;
- try {
- final Uri.Builder builder = getContentUriBuilderForType(clientId, client,
- QUERY_PATH_DICT_INFO, locale.toString());
- if (!hasDefaultWordList) {
- builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER,
- QUERY_PARAMETER_TRUE);
- }
- final Uri queryUri = builder.build();
- final boolean isProtocolV2 = (QUERY_PARAMETER_PROTOCOL_VALUE.equals(
- queryUri.getQueryParameter(QUERY_PARAMETER_PROTOCOL)));
-
- cursor = client.query(queryUri, DICTIONARY_PROJECTION, null, null, null);
- if (isProtocolV2 && null == cursor) {
- reinitializeClientRecordInDictionaryContentProvider(context, client, clientId);
- cursor = client.query(queryUri, DICTIONARY_PROJECTION, null, null, null);
- }
- if (null == cursor) return Collections.<WordListInfo>emptyList();
- if (cursor.getCount() <= 0 || !cursor.moveToFirst()) {
- return Collections.<WordListInfo>emptyList();
- }
- final ArrayList<WordListInfo> list = new ArrayList<>();
- do {
- final String wordListId = cursor.getString(0);
- final String wordListLocale = cursor.getString(1);
- final String wordListRawChecksum = cursor.getString(2);
- if (TextUtils.isEmpty(wordListId)) continue;
- list.add(new WordListInfo(wordListId, wordListLocale, wordListRawChecksum));
- } while (cursor.moveToNext());
- return list;
- } catch (RemoteException e) {
- // The documentation is unclear as to in which cases this may happen, but it probably
- // happens when the content provider got suddenly killed because it crashed or because
- // the user disabled it through Settings.
- Log.e(TAG, "RemoteException: communication with the dictionary pack cut", e);
- return Collections.<WordListInfo>emptyList();
- } catch (Exception e) {
- // A crash here is dangerous because crashing here would brick any encrypted device -
- // we need the keyboard to be up and working to enter the password, so we don't want
- // to die no matter what. So let's be as safe as possible.
- Log.e(TAG, "Unexpected exception communicating with the dictionary pack", e);
- return Collections.<WordListInfo>emptyList();
- } finally {
- if (null != cursor) {
- cursor.close();
- }
- client.release();
- }
- }
-
-
- /**
- * Helper method to encapsulate exception handling.
- */
- private static AssetFileDescriptor openAssetFileDescriptor(
- final ContentProviderClient providerClient, final Uri uri) {
- try {
- return providerClient.openAssetFile(uri, "r");
- } catch (FileNotFoundException e) {
- // I don't want to log the word list URI here for security concerns. The exception
- // contains the name of the file, so let's not pass it to Log.e here.
- Log.e(TAG, "Could not find a word list from the dictionary provider."
- /* intentionally don't pass the exception (see comment above) */);
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Can't communicate with the dictionary pack", e);
- return null;
- }
- }
-
- /**
- * Stages a word list the id of which is passed as an argument. This will write the file
- * to the cache file name designated by its id and locale, overwriting it if already present
- * and creating it (and its containing directory) if necessary.
- */
- private static void installWordListToStaging(final String wordlistId, final String locale,
- final String rawChecksum, final ContentProviderClient providerClient,
- final Context context) {
- final int COMPRESSED_CRYPTED_COMPRESSED = 0;
- final int CRYPTED_COMPRESSED = 1;
- final int COMPRESSED_CRYPTED = 2;
- final int COMPRESSED_ONLY = 3;
- final int CRYPTED_ONLY = 4;
- final int NONE = 5;
- final int MODE_MIN = COMPRESSED_CRYPTED_COMPRESSED;
- final int MODE_MAX = NONE;
-
- final String clientId = context.getString(R.string.dictionary_pack_client_id);
- final Uri.Builder wordListUriBuilder;
- try {
- wordListUriBuilder = getContentUriBuilderForType(clientId,
- providerClient, QUERY_PATH_DATAFILE, wordlistId /* extraPath */);
- } catch (RemoteException e) {
- Log.e(TAG, "Can't communicate with the dictionary pack", e);
- return;
- }
- final String finalFileName =
- DictionaryInfoUtils.getStagingFileName(wordlistId, locale, context);
- String tempFileName;
- try {
- tempFileName = BinaryDictionaryGetter.getTempFileName(wordlistId, context);
- } catch (IOException e) {
- Log.e(TAG, "Can't open the temporary file", e);
- return;
- }
-
- for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
- final InputStream originalSourceStream;
- InputStream inputStream = null;
- InputStream uncompressedStream = null;
- InputStream decryptedStream = null;
- BufferedInputStream bufferedInputStream = null;
- File outputFile = null;
- BufferedOutputStream bufferedOutputStream = null;
- AssetFileDescriptor afd = null;
- final Uri wordListUri = wordListUriBuilder.build();
- try {
- // Open input.
- afd = openAssetFileDescriptor(providerClient, wordListUri);
- // If we can't open it at all, don't even try a number of times.
- if (null == afd) return;
- originalSourceStream = afd.createInputStream();
- // Open output.
- outputFile = new File(tempFileName);
- // Just to be sure, delete the file. This may fail silently, and return false: this
- // is the right thing to do, as we just want to continue anyway.
- outputFile.delete();
- // Get the appropriate decryption method for this try
- switch (mode) {
- case COMPRESSED_CRYPTED_COMPRESSED:
- uncompressedStream =
- FileTransforms.getUncompressedStream(originalSourceStream);
- decryptedStream = FileTransforms.getDecryptedStream(uncompressedStream);
- inputStream = FileTransforms.getUncompressedStream(decryptedStream);
- break;
- case CRYPTED_COMPRESSED:
- decryptedStream = FileTransforms.getDecryptedStream(originalSourceStream);
- inputStream = FileTransforms.getUncompressedStream(decryptedStream);
- break;
- case COMPRESSED_CRYPTED:
- uncompressedStream =
- FileTransforms.getUncompressedStream(originalSourceStream);
- inputStream = FileTransforms.getDecryptedStream(uncompressedStream);
- break;
- case COMPRESSED_ONLY:
- inputStream = FileTransforms.getUncompressedStream(originalSourceStream);
- break;
- case CRYPTED_ONLY:
- inputStream = FileTransforms.getDecryptedStream(originalSourceStream);
- break;
- case NONE:
- inputStream = originalSourceStream;
- break;
- }
- bufferedInputStream = new BufferedInputStream(inputStream);
- bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
- checkMagicAndCopyFileTo(bufferedInputStream, bufferedOutputStream);
- bufferedOutputStream.flush();
- bufferedOutputStream.close();
-
- if (SHOULD_VERIFY_CHECKSUM) {
- final String actualRawChecksum = MD5Calculator.checksum(
- new BufferedInputStream(new FileInputStream(outputFile)));
- Log.i(TAG, "Computed checksum for downloaded dictionary. Expected = "
- + rawChecksum + " ; actual = " + actualRawChecksum);
- if (!TextUtils.isEmpty(rawChecksum) && !rawChecksum.equals(actualRawChecksum)) {
- throw new IOException(
- "Could not decode the file correctly : checksum differs");
- }
- }
-
- // move the output file to the final staging file.
- final File finalFile = new File(finalFileName);
- if (!FileUtils.renameTo(outputFile, finalFile)) {
- Log.e(TAG, String.format("Failed to rename from %s to %s.",
- outputFile.getAbsoluteFile(), finalFile.getAbsoluteFile()));
- }
-
- wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT,
- QUERY_PARAMETER_SUCCESS);
- if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) {
- Log.e(TAG, "Could not have the dictionary pack delete a word list");
- }
- Log.d(TAG, "Successfully copied file for wordlist ID " + wordlistId);
- // Success! Close files (through the finally{} clause) and return.
- return;
- } catch (Exception e) {
- if (DEBUG) {
- Log.e(TAG, "Can't open word list in mode " + mode, e);
- }
- if (null != outputFile) {
- // This may or may not fail. The file may not have been created if the
- // exception was thrown before it could be. Hence, both failure and
- // success are expected outcomes, so we don't check the return value.
- outputFile.delete();
- }
- // Try the next method.
- } finally {
- // Ignore exceptions while closing files.
- closeAssetFileDescriptorAndReportAnyException(afd);
- closeCloseableAndReportAnyException(inputStream);
- closeCloseableAndReportAnyException(uncompressedStream);
- closeCloseableAndReportAnyException(decryptedStream);
- closeCloseableAndReportAnyException(bufferedInputStream);
- closeCloseableAndReportAnyException(bufferedOutputStream);
- }
- }
-
- // We could not copy the file at all. This is very unexpected.
- // I'd rather not print the word list ID to the log out of security concerns
- Log.e(TAG, "Could not copy a word list. Will not be able to use it.");
- // If we can't copy it we should warn the dictionary provider so that it can mark it
- // as invalid.
- reportBrokenFileToDictionaryProvider(providerClient, clientId, wordlistId);
- }
-
- public static boolean reportBrokenFileToDictionaryProvider(
- final ContentProviderClient providerClient, final String clientId,
- final String wordlistId) {
- try {
- final Uri.Builder wordListUriBuilder = getContentUriBuilderForType(clientId,
- providerClient, QUERY_PATH_DATAFILE, wordlistId /* extraPath */);
- wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT,
- QUERY_PARAMETER_FAILURE);
- if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) {
- Log.e(TAG, "Unable to delete a word list.");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Communication with the dictionary provider was cut", e);
- return false;
- }
- return true;
- }
-
- // Ideally the two following methods should be merged, but AssetFileDescriptor does not
- // implement Closeable although it does implement #close(), and Java does not have
- // structural typing.
- private static void closeAssetFileDescriptorAndReportAnyException(
- final AssetFileDescriptor file) {
- try {
- if (null != file) file.close();
- } catch (Exception e) {
- Log.e(TAG, "Exception while closing a file", e);
- }
- }
-
- private static void closeCloseableAndReportAnyException(final Closeable file) {
- try {
- if (null != file) file.close();
- } catch (Exception e) {
- Log.e(TAG, "Exception while closing a file", e);
- }
- }
-
- /**
- * Queries a content provider for word list data for some locale and stage the returned files
- *
- * This will query a content provider for word list data for a given locale, and copy the
- * files locally so that they can be mmap'ed. This may overwrite previously cached word lists
- * with newer versions if a newer version is made available by the content provider.
- * @throw FileNotFoundException if the provider returns non-existent data.
- * @throw IOException if the provider-returned data could not be read.
- */
- public static void installDictToStagingFromContentProvider(final Locale locale,
- final Context context, final boolean hasDefaultWordList) {
- final ContentProviderClient providerClient;
- try {
- providerClient = context.getContentResolver().
- acquireContentProviderClient(getProviderUriBuilder("").build());
- } catch (final SecurityException e) {
- Log.e(TAG, "No permission to communicate with the dictionary provider", e);
- return;
- }
- if (null == providerClient) {
- Log.e(TAG, "Can't establish communication with the dictionary provider");
- return;
- }
- try {
- final List<WordListInfo> idList = getWordListWordListInfos(locale, context,
- hasDefaultWordList);
- for (WordListInfo id : idList) {
- installWordListToStaging(id.mId, id.mLocale, id.mRawChecksum, providerClient,
- context);
- }
- } finally {
- providerClient.release();
- }
- }
-
- /**
- * Downloads the dictionary if it was never requested/used.
- *
- * @param locale locale to download
- * @param context the context for resources and providers.
- * @param hasDefaultWordList whether the default wordlist exists in the resources.
- */
- public static void downloadDictIfNeverRequested(final Locale locale,
- final Context context, final boolean hasDefaultWordList) {
- getWordListWordListInfos(locale, context, hasDefaultWordList);
- }
-
- /**
- * Copies the data in an input stream to a target file if the magic number matches.
- *
- * If the magic number does not match the expected value, this method throws an
- * IOException. Other usual conditions for IOException or FileNotFoundException
- * also apply.
- *
- * @param input the stream to be copied.
- * @param output an output stream to copy the data to.
- */
- public static void checkMagicAndCopyFileTo(final BufferedInputStream input,
- final BufferedOutputStream output) throws FileNotFoundException, IOException {
- // Check the magic number
- final int length = MAGIC_NUMBER_VERSION_2.length;
- final byte[] magicNumberBuffer = new byte[length];
- final int readMagicNumberSize = input.read(magicNumberBuffer, 0, length);
- if (readMagicNumberSize < length) {
- throw new IOException("Less bytes to read than the magic number length");
- }
- if (SHOULD_VERIFY_MAGIC_NUMBER) {
- if (!Arrays.equals(MAGIC_NUMBER_VERSION_2, magicNumberBuffer)) {
- if (!Arrays.equals(MAGIC_NUMBER_VERSION_1, magicNumberBuffer)) {
- throw new IOException("Wrong magic number for downloaded file");
- }
- }
- }
- output.write(magicNumberBuffer);
-
- // Actually copy the file
- final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE];
- for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) {
- output.write(buffer, 0, readBytes);
- }
- input.close();
- }
-
- private static void reinitializeClientRecordInDictionaryContentProvider(final Context context,
- final ContentProviderClient client, final String clientId) throws RemoteException {
- final String metadataFileUri = MetadataFileUriGetter.getMetadataUri(context);
- Log.i(TAG, "reinitializeClientRecordInDictionaryContentProvider() : MetadataFileUri = "
- + metadataFileUri);
- final String metadataAdditionalId = MetadataFileUriGetter.getMetadataAdditionalId(context);
- // Tell the content provider to reset all information about this client id
- final Uri metadataContentUri = getProviderUriBuilder(clientId)
- .appendPath(QUERY_PATH_METADATA)
- .appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE)
- .build();
- client.delete(metadataContentUri, null, null);
- // Update the metadata URI
- final ContentValues metadataValues = new ContentValues();
- metadataValues.put(INSERT_METADATA_CLIENT_ID_COLUMN, clientId);
- metadataValues.put(INSERT_METADATA_METADATA_URI_COLUMN, metadataFileUri);
- metadataValues.put(INSERT_METADATA_METADATA_ADDITIONAL_ID_COLUMN, metadataAdditionalId);
- client.insert(metadataContentUri, metadataValues);
-
- // Update the dictionary list.
- final Uri dictionaryContentUriBase = getProviderUriBuilder(clientId)
- .appendPath(QUERY_PATH_DICT_INFO)
- .appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE)
- .build();
- final ArrayList<DictionaryInfo> dictionaryList =
- DictionaryInfoUtils.getCurrentDictionaryFileNameAndVersionInfo(context);
- final int length = dictionaryList.size();
- for (int i = 0; i < length; ++i) {
- final DictionaryInfo info = dictionaryList.get(i);
- Log.i(TAG, "reinitializeClientRecordInDictionaryContentProvider() : Insert " + info);
- client.insert(Uri.withAppendedPath(dictionaryContentUriBase, info.mId),
- info.toContentValues());
- }
-
- // Read from metadata file in resources to get the baseline dictionary info.
- // This ensures we start with a valid list of available dictionaries.
- final int metadataResourceId = context.getResources().getIdentifier("metadata",
- "raw", DictionaryInfoUtils.RESOURCE_PACKAGE_NAME);
- if (metadataResourceId == 0) {
- Log.w(TAG, "Missing metadata.json resource");
- return;
- }
- InputStream inputStream = null;
- try {
- inputStream = context.getResources().openRawResource(metadataResourceId);
- UpdateHandler.handleMetadata(context, inputStream, clientId);
- } catch (Exception e) {
- Log.w(TAG, "Failed to read metadata.json from resources", e);
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException e) {
- Log.w(TAG, "Failed to close metadata.json", e);
- }
- }
- }
- }
-
- /**
- * Initialize a client record with the dictionary content provider.
- *
- * This merely acquires the content provider and calls
- * #reinitializeClientRecordInDictionaryContentProvider.
- *
- * @param context the context for resources and providers.
- * @param clientId the client ID to use.
- */
- public static void initializeClientRecordHelper(final Context context, final String clientId) {
- try {
- final ContentProviderClient client = context.getContentResolver().
- acquireContentProviderClient(getProviderUriBuilder("").build());
- if (null == client) return;
- reinitializeClientRecordInDictionaryContentProvider(context, client, clientId);
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot contact the dictionary content provider", e);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
deleted file mode 100644
index c13f0e20a..000000000
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.res.AssetFileDescriptor;
-import android.util.Log;
-
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.BufferUnderflowException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-
-/**
- * Helper class to get the address of a mmap'able dictionary file.
- */
-final public class BinaryDictionaryGetter {
-
- /**
- * Used for Log actions from this class
- */
- private static final String TAG = BinaryDictionaryGetter.class.getSimpleName();
-
- /**
- * Used to return empty lists
- */
- private static final File[] EMPTY_FILE_ARRAY = new File[0];
-
- /**
- * Name of the common preferences name to know which word list are on and which are off.
- */
- private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
-
- private static final boolean SHOULD_USE_DICT_VERSION =
- DecoderSpecificConstants.SHOULD_USE_DICT_VERSION;
-
- // Name of the category for the main dictionary
- public static final String MAIN_DICTIONARY_CATEGORY = "main";
- public static final String ID_CATEGORY_SEPARATOR = ":";
-
- // The key considered to read the version attribute in a dictionary file.
- private static String VERSION_KEY = "version";
-
- // Prevents this from being instantiated
- private BinaryDictionaryGetter() {}
-
- /**
- * Generates a unique temporary file name in the app cache directory.
- */
- public static String getTempFileName(final String id, final Context context)
- throws IOException {
- final String safeId = DictionaryInfoUtils.replaceFileNameDangerousCharacters(id);
- final File directory = new File(DictionaryInfoUtils.getWordListTempDirectory(context));
- if (!directory.exists()) {
- if (!directory.mkdirs()) {
- Log.e(TAG, "Could not create the temporary directory");
- }
- }
- // If the first argument is less than three chars, createTempFile throws a
- // RuntimeException. We don't really care about what name we get, so just
- // put a three-chars prefix makes us safe.
- return File.createTempFile("xxx" + safeId, null, directory).getAbsolutePath();
- }
-
- /**
- * Returns a file address from a resource, or null if it cannot be opened.
- */
- public static AssetFileAddress loadFallbackResource(final Context context,
- final int fallbackResId) {
- AssetFileDescriptor afd = null;
- try {
- afd = context.getResources().openRawResourceFd(fallbackResId);
- } catch (RuntimeException e) {
- Log.e(TAG, "Resource not found: " + fallbackResId);
- return null;
- }
- if (afd == null) {
- Log.e(TAG, "Resource cannot be opened: " + fallbackResId);
- return null;
- }
- try {
- return AssetFileAddress.makeFromFileNameAndOffset(
- context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
- } finally {
- try {
- afd.close();
- } catch (IOException ignored) {
- }
- }
- }
-
- private static final class DictPackSettings {
- final SharedPreferences mDictPreferences;
- public DictPackSettings(final Context context) {
- mDictPreferences = null == context ? null
- : context.getSharedPreferences(COMMON_PREFERENCES_NAME,
- Context.MODE_MULTI_PROCESS);
- }
- public boolean isWordListActive(final String dictId) {
- if (null == mDictPreferences) {
- // If we don't have preferences it basically means we can't find the dictionary
- // pack - either it's not installed, or it's disabled, or there is some strange
- // bug. Either way, a word list with no settings should be on by default: default
- // dictionaries in LatinIME are on if there is no settings at all, and if for some
- // reason some dictionaries have been installed BUT the dictionary pack can't be
- // found anymore it's safer to actually supply installed dictionaries.
- return true;
- }
- // The default is true here for the same reasons as above. We got the dictionary
- // pack but if we don't have any settings for it it means the user has never been
- // to the settings yet. So by default, the main dictionaries should be on.
- return mDictPreferences.getBoolean(dictId, true);
- }
- }
-
- /**
- * Utility class for the {@link #getCachedWordLists} method
- */
- private static final class FileAndMatchLevel {
- final File mFile;
- final int mMatchLevel;
- public FileAndMatchLevel(final File file, final int matchLevel) {
- mFile = file;
- mMatchLevel = matchLevel;
- }
- }
-
- /**
- * Returns the list of cached files for a specific locale, one for each category.
- *
- * This will return exactly one file for each word list category that matches
- * the passed locale. If several files match the locale for any given category,
- * this returns the file with the closest match to the locale. For example, if
- * the passed word list is en_US, and for a category we have an en and an en_US
- * word list available, we'll return only the en_US one.
- * Thus, the list will contain as many files as there are categories.
- *
- * @param locale the locale to find the dictionary files for, as a string.
- * @param context the context on which to open the files upon.
- * @return an array of binary dictionary files, which may be empty but may not be null.
- */
- public static File[] getCachedWordLists(final String locale, final Context context) {
- final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
- if (null == directoryList) return EMPTY_FILE_ARRAY;
- final HashMap<String, FileAndMatchLevel> cacheFiles = new HashMap<>();
- for (File directory : directoryList) {
- if (!directory.isDirectory()) continue;
- final String dirLocale =
- DictionaryInfoUtils.getWordListIdFromFileName(directory.getName());
- final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale);
- if (LocaleUtils.isMatch(matchLevel)) {
- final File[] wordLists = directory.listFiles();
- if (null != wordLists) {
- for (File wordList : wordLists) {
- final String category =
- DictionaryInfoUtils.getCategoryFromFileName(wordList.getName());
- final FileAndMatchLevel currentBestMatch = cacheFiles.get(category);
- if (null == currentBestMatch || currentBestMatch.mMatchLevel < matchLevel) {
- cacheFiles.put(category, new FileAndMatchLevel(wordList, matchLevel));
- }
- }
- }
- }
- }
- if (cacheFiles.isEmpty()) return EMPTY_FILE_ARRAY;
- final File[] result = new File[cacheFiles.size()];
- int index = 0;
- for (final FileAndMatchLevel entry : cacheFiles.values()) {
- result[index++] = entry.mFile;
- }
- return result;
- }
-
- // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since
- // those do not include allowlist entries, the new code with an old version of the dictionary
- // would lose allowlist functionality.
- private static boolean hackCanUseDictionaryFile(final File file) {
- if (!SHOULD_USE_DICT_VERSION) {
- return true;
- }
-
- try {
- // Read the version of the file
- final DictionaryHeader header = BinaryDictionaryUtils.getHeader(file);
- final String version = header.mDictionaryOptions.mAttributes.get(VERSION_KEY);
- if (null == version) {
- // No version in the options : the format is unexpected
- return false;
- }
- // Version 18 is the first one to include the allowlist.
- // Obviously this is a big ## HACK ##
- return Integer.parseInt(version) >= 18;
- } catch (java.io.FileNotFoundException e) {
- return false;
- } catch (java.io.IOException e) {
- return false;
- } catch (NumberFormatException e) {
- return false;
- } catch (BufferUnderflowException e) {
- return false;
- } catch (UnsupportedFormatException e) {
- return false;
- }
- }
-
- /**
- * Returns a list of file addresses for a given locale, trying relevant methods in order.
- *
- * Tries to get binary dictionaries from various sources, in order:
- * - Uses a content provider to get a public dictionary set, as per the protocol described
- * in BinaryDictionaryFileDumper.
- * If that fails:
- * - Gets a file name from the built-in dictionary for this locale, if any.
- * If that fails:
- * - Returns null.
- * @return The list of addresses of valid dictionary files, or null.
- */
- public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale,
- final Context context, boolean notifyDictionaryPackForUpdates) {
- if (notifyDictionaryPackForUpdates) {
- final boolean hasDefaultWordList = DictionaryInfoUtils.isDictionaryAvailable(
- context, locale);
- // It makes sure that the first time keyboard comes up and the dictionaries are reset,
- // the DB is populated with the appropriate values for each locale. Helps in downloading
- // the dictionaries when the user enables and switches new languages before the
- // DictionaryService runs.
- BinaryDictionaryFileDumper.downloadDictIfNeverRequested(
- locale, context, hasDefaultWordList);
-
- // Move a staging files to the cache ddirectories if any.
- DictionaryInfoUtils.moveStagingFilesIfExists(context);
- }
- final File[] cachedWordLists = getCachedWordLists(locale.toString(), context);
- final String mainDictId = DictionaryInfoUtils.getMainDictId(locale);
- final DictPackSettings dictPackSettings = new DictPackSettings(context);
-
- boolean foundMainDict = false;
- final ArrayList<AssetFileAddress> fileList = new ArrayList<>();
- // cachedWordLists may not be null, see doc for getCachedDictionaryList
- for (final File f : cachedWordLists) {
- final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName());
- final boolean canUse = f.canRead() && hackCanUseDictionaryFile(f);
- if (canUse && DictionaryInfoUtils.isMainWordListId(wordListId)) {
- foundMainDict = true;
- }
- if (!dictPackSettings.isWordListActive(wordListId)) continue;
- if (canUse) {
- final AssetFileAddress afa = AssetFileAddress.makeFromFileName(f.getPath());
- if (null != afa) fileList.add(afa);
- } else {
- Log.e(TAG, "Found a cached dictionary file for " + locale.toString()
- + " but cannot read or use it");
- }
- }
-
- if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) {
- final int fallbackResId =
- DictionaryInfoUtils.getMainDictionaryResourceId(context.getResources(), locale);
- final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId);
- if (null != fallbackAsset) {
- fileList.add(fallbackAsset);
- }
- }
-
- return fileList;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
deleted file mode 100644
index dbd639fe8..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.Manifest;
-import android.content.Context;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.latin.ContactsManager.ContactsChangedListener;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.personalization.AccountUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-import javax.annotation.Nullable;
-
-public class ContactsBinaryDictionary extends ExpandableBinaryDictionary
- implements ContactsChangedListener {
- private static final String TAG = ContactsBinaryDictionary.class.getSimpleName();
- private static final String NAME = "contacts";
-
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_DUMP = false;
-
- /**
- * Whether to use "firstname lastname" in bigram predictions.
- */
- private final boolean mUseFirstLastBigrams;
- private final ContactsManager mContactsManager;
-
- protected ContactsBinaryDictionary(final Context context, final Locale locale,
- final File dictFile, final String name) {
- super(context, getDictName(name, locale, dictFile), locale, Dictionary.TYPE_CONTACTS,
- dictFile);
- mUseFirstLastBigrams = ContactsDictionaryUtils.useFirstLastBigramsForLocale(locale);
- mContactsManager = new ContactsManager(context);
- mContactsManager.registerForUpdates(this /* listener */);
- reloadDictionaryIfRequired();
- }
-
- // Note: This method is called by {@link DictionaryFacilitator} using Java reflection.
- @ExternallyReferenced
- public static ContactsBinaryDictionary getDictionary(final Context context, final Locale locale,
- final File dictFile, final String dictNamePrefix, @Nullable final String account) {
- return new ContactsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME);
- }
-
- @Override
- public synchronized void close() {
- mContactsManager.close();
- super.close();
- }
-
- /**
- * Typically called whenever the dictionary is created for the first time or
- * recreated when we think that there are updates to the dictionary.
- * This is called asynchronously.
- */
- @Override
- public void loadInitialContentsLocked() {
- loadDeviceAccountsEmailAddressesLocked();
- loadDictionaryForUriLocked(ContactsContract.Profile.CONTENT_URI);
- // TODO: Switch this URL to the newer ContactsContract too
- loadDictionaryForUriLocked(Contacts.CONTENT_URI);
- }
-
- /**
- * Loads device accounts to the dictionary.
- */
- private void loadDeviceAccountsEmailAddressesLocked() {
- final List<String> accountVocabulary =
- AccountUtils.getDeviceAccountsEmailAddresses(mContext);
- if (accountVocabulary == null || accountVocabulary.isEmpty()) {
- return;
- }
- for (String word : accountVocabulary) {
- if (DEBUG) {
- Log.d(TAG, "loadAccountVocabulary: " + word);
- }
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addUnigramLocked(word, ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS,
- false /* isNotAWord */, false /* isPossiblyOffensive */,
- BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- }
- }
-
- /**
- * Loads data within content providers to the dictionary.
- */
- private void loadDictionaryForUriLocked(final Uri uri) {
- if (!PermissionsUtil.checkAllPermissionsGranted(
- mContext, Manifest.permission.READ_CONTACTS)) {
- Log.i(TAG, "No permission to read contacts. Not loading the Dictionary.");
- }
-
- final ArrayList<String> validNames = mContactsManager.getValidNames(uri);
- for (final String name : validNames) {
- addNameLocked(name);
- }
- if (uri.equals(Contacts.CONTENT_URI)) {
- // Since we were able to add content successfully, update the local
- // state of the manager.
- mContactsManager.updateLocalState(validNames);
- }
- }
-
- /**
- * Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their
- * bigrams depending on locale.
- */
- private void addNameLocked(final String name) {
- int len = StringUtils.codePointCount(name);
- NgramContext ngramContext = NgramContext.getEmptyPrevWordsContext(
- BinaryDictionary.MAX_PREV_WORD_COUNT_FOR_N_GRAM);
- // TODO: Better tokenization for non-Latin writing systems
- for (int i = 0; i < len; i++) {
- if (Character.isLetter(name.codePointAt(i))) {
- int end = ContactsDictionaryUtils.getWordEndPosition(name, len, i);
- String word = name.substring(i, end);
- if (DEBUG_DUMP) {
- Log.d(TAG, "addName word = " + word);
- }
- i = end - 1;
- // Don't add single letter words, possibly confuses
- // capitalization of i.
- final int wordLen = StringUtils.codePointCount(word);
- if (wordLen <= MAX_WORD_LENGTH && wordLen > 1) {
- if (DEBUG) {
- Log.d(TAG, "addName " + name + ", " + word + ", " + ngramContext);
- }
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addUnigramLocked(word,
- ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS, false /* isNotAWord */,
- false /* isPossiblyOffensive */,
- BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- if (ngramContext.isValid() && mUseFirstLastBigrams) {
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addNgramEntryLocked(ngramContext,
- word,
- ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS_BIGRAM,
- BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- }
- ngramContext = ngramContext.getNextNgramContext(
- new NgramContext.WordInfo(word));
- }
- }
- }
- }
-
- @Override
- public void onContactsChange() {
- setNeedsToRecreate();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ContactsContentObserver.java b/java/src/com/android/inputmethod/latin/ContactsContentObserver.java
deleted file mode 100644
index 6103a8296..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsContentObserver.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.Manifest;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.SystemClock;
-import android.provider.ContactsContract.Contacts;
-import android.util.Log;
-
-import com.android.inputmethod.latin.ContactsManager.ContactsChangedListener;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.utils.ExecutorUtils;
-
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A content observer that listens to updates to content provider {@link Contacts#CONTENT_URI}.
- */
-public class ContactsContentObserver implements Runnable {
- private static final String TAG = "ContactsContentObserver";
-
- private final Context mContext;
- private final ContactsManager mManager;
- private final AtomicBoolean mRunning = new AtomicBoolean(false);
-
- private ContentObserver mContentObserver;
- private ContactsChangedListener mContactsChangedListener;
-
- public ContactsContentObserver(final ContactsManager manager, final Context context) {
- mManager = manager;
- mContext = context;
- }
-
- public void registerObserver(final ContactsChangedListener listener) {
- if (!PermissionsUtil.checkAllPermissionsGranted(
- mContext, Manifest.permission.READ_CONTACTS)) {
- Log.i(TAG, "No permission to read contacts. Not registering the observer.");
- // do nothing if we do not have the permission to read contacts.
- return;
- }
-
- if (DebugFlags.DEBUG_ENABLED) {
- Log.d(TAG, "registerObserver()");
- }
- mContactsChangedListener = listener;
- mContentObserver = new ContentObserver(null /* handler */) {
- @Override
- public void onChange(boolean self) {
- ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD)
- .execute(ContactsContentObserver.this);
- }
- };
- final ContentResolver contentResolver = mContext.getContentResolver();
- contentResolver.registerContentObserver(Contacts.CONTENT_URI, true, mContentObserver);
- }
-
- @Override
- public void run() {
- if (!PermissionsUtil.checkAllPermissionsGranted(
- mContext, Manifest.permission.READ_CONTACTS)) {
- Log.i(TAG, "No permission to read contacts. Not updating the contacts.");
- unregister();
- return;
- }
-
- if (!mRunning.compareAndSet(false /* expect */, true /* update */)) {
- if (DebugFlags.DEBUG_ENABLED) {
- Log.d(TAG, "run() : Already running. Don't waste time checking again.");
- }
- return;
- }
- if (haveContentsChanged()) {
- if (DebugFlags.DEBUG_ENABLED) {
- Log.d(TAG, "run() : Contacts have changed. Notifying listeners.");
- }
- mContactsChangedListener.onContactsChange();
- }
- mRunning.set(false);
- }
-
- boolean haveContentsChanged() {
- if (!PermissionsUtil.checkAllPermissionsGranted(
- mContext, Manifest.permission.READ_CONTACTS)) {
- Log.i(TAG, "No permission to read contacts. Marking contacts as not changed.");
- return false;
- }
-
- final long startTime = SystemClock.uptimeMillis();
- final int contactCount = mManager.getContactCount();
- if (contactCount > ContactsDictionaryConstants.MAX_CONTACTS_PROVIDER_QUERY_LIMIT) {
- // If there are too many contacts then return false. In this rare case it is impossible
- // to include all of them anyways and the cost of rebuilding the dictionary is too high.
- // TODO: Sort and check only the most recent contacts?
- return false;
- }
- if (contactCount != mManager.getContactCountAtLastRebuild()) {
- if (DebugFlags.DEBUG_ENABLED) {
- Log.d(TAG, "haveContentsChanged() : Count changed from "
- + mManager.getContactCountAtLastRebuild() + " to " + contactCount);
- }
- return true;
- }
- final ArrayList<String> names = mManager.getValidNames(Contacts.CONTENT_URI);
- if (names.hashCode() != mManager.getHashCodeAtLastRebuild()) {
- return true;
- }
- if (DebugFlags.DEBUG_ENABLED) {
- Log.d(TAG, "haveContentsChanged() : No change detected in "
- + (SystemClock.uptimeMillis() - startTime) + " ms)");
- }
- return false;
- }
-
- public void unregister() {
- mContext.getContentResolver().unregisterContentObserver(mContentObserver);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionaryConstants.java b/java/src/com/android/inputmethod/latin/ContactsDictionaryConstants.java
deleted file mode 100644
index 022940910..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsDictionaryConstants.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.provider.BaseColumns;
-import android.provider.ContactsContract.Contacts;
-
-/**
- * Constants related to Contacts Content Provider.
- */
-public class ContactsDictionaryConstants {
- /**
- * Projections for {@link Contacts.CONTENT_URI}
- */
- public static final String[] PROJECTION = { BaseColumns._ID, Contacts.DISPLAY_NAME,
- Contacts.TIMES_CONTACTED, Contacts.LAST_TIME_CONTACTED, Contacts.IN_VISIBLE_GROUP };
- public static final String[] PROJECTION_ID_ONLY = { BaseColumns._ID };
-
- /**
- * Frequency for contacts information into the dictionary
- */
- public static final int FREQUENCY_FOR_CONTACTS = 40;
- public static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
-
- /**
- * Do not attempt to query contacts if there are more than this many entries.
- */
- public static final int MAX_CONTACTS_PROVIDER_QUERY_LIMIT = 10000;
-
- /**
- * Index of the column for 'name' in content providers:
- * Contacts & ContactsContract.Profile.
- */
- public static final int NAME_INDEX = 1;
- public static final int TIMES_CONTACTED_INDEX = 2;
- public static final int LAST_TIME_CONTACTED_INDEX = 3;
- public static final int IN_VISIBLE_GROUP_INDEX = 4;
-}
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionaryUtils.java b/java/src/com/android/inputmethod/latin/ContactsDictionaryUtils.java
deleted file mode 100644
index b77388434..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsDictionaryUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.common.Constants;
-
-import java.util.Locale;
-
-/**
- * Utility methods related contacts dictionary.
- */
-public class ContactsDictionaryUtils {
-
- /**
- * Returns the index of the last letter in the word, starting from position startIndex.
- */
- public static int getWordEndPosition(final String string, final int len,
- final int startIndex) {
- int end;
- int cp = 0;
- for (end = startIndex + 1; end < len; end += Character.charCount(cp)) {
- cp = string.codePointAt(end);
- if (cp != Constants.CODE_DASH && cp != Constants.CODE_SINGLE_QUOTE
- && !Character.isLetter(cp)) {
- break;
- }
- }
- return end;
- }
-
- /**
- * Returns true if the locale supports using first name and last name as bigrams.
- */
- public static boolean useFirstLastBigramsForLocale(final Locale locale) {
- // TODO: Add firstname/lastname bigram rules for other languages.
- if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
- return true;
- }
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ContactsManager.java b/java/src/com/android/inputmethod/latin/ContactsManager.java
deleted file mode 100644
index 13503ff45..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsManager.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Manages all interactions with Contacts DB.
- *
- * The manager provides an API for listening to meaning full updates by keeping a
- * measure of the current state of the content provider.
- */
-public class ContactsManager {
- private static final String TAG = "ContactsManager";
-
- /**
- * Use at most this many of the highest affinity contacts.
- */
- public static final int MAX_CONTACT_NAMES = 200;
-
- protected static class RankedContact {
- public final String mName;
- public final long mLastContactedTime;
- public final int mTimesContacted;
- public final boolean mInVisibleGroup;
-
- private float mAffinity = 0.0f;
-
- RankedContact(final Cursor cursor) {
- mName = cursor.getString(
- ContactsDictionaryConstants.NAME_INDEX);
- mTimesContacted = cursor.getInt(
- ContactsDictionaryConstants.TIMES_CONTACTED_INDEX);
- mLastContactedTime = cursor.getLong(
- ContactsDictionaryConstants.LAST_TIME_CONTACTED_INDEX);
- mInVisibleGroup = cursor.getInt(
- ContactsDictionaryConstants.IN_VISIBLE_GROUP_INDEX) == 1;
- }
-
- float getAffinity() {
- return mAffinity;
- }
-
- /**
- * Calculates the affinity with the contact based on:
- * - How many times it has been contacted
- * - How long since the last contact.
- * - Whether the contact is in the visible group (i.e., Contacts list).
- *
- * Note: This affinity is limited by the fact that some apps currently do not update the
- * LAST_TIME_CONTACTED or TIMES_CONTACTED counters. As a result, a frequently messaged
- * contact may still have 0 affinity.
- */
- void computeAffinity(final int maxTimesContacted, final long currentTime) {
- final float timesWeight = ((float) mTimesContacted + 1) / (maxTimesContacted + 1);
- final long timeSinceLastContact = Math.min(
- Math.max(0, currentTime - mLastContactedTime),
- TimeUnit.MILLISECONDS.convert(180, TimeUnit.DAYS));
- final float lastTimeWeight = (float) Math.pow(0.5,
- timeSinceLastContact / (TimeUnit.MILLISECONDS.convert(10, TimeUnit.DAYS)));
- final float visibleWeight = mInVisibleGroup ? 1.0f : 0.0f;
- mAffinity = (timesWeight + lastTimeWeight + visibleWeight) / 3;
- }
- }
-
- private static class AffinityComparator implements Comparator<RankedContact> {
- @Override
- public int compare(RankedContact contact1, RankedContact contact2) {
- return Float.compare(contact2.getAffinity(), contact1.getAffinity());
- }
- }
-
- /**
- * Interface to implement for classes interested in getting notified for updates
- * to Contacts content provider.
- */
- public static interface ContactsChangedListener {
- public void onContactsChange();
- }
-
- /**
- * The number of contacts observed in the most recent instance of
- * contacts content provider.
- */
- private AtomicInteger mContactCountAtLastRebuild = new AtomicInteger(0);
-
- /**
- * The hash code of list of valid contacts names in the most recent dictionary
- * rebuild.
- */
- private AtomicInteger mHashCodeAtLastRebuild = new AtomicInteger(0);
-
- private final Context mContext;
- private final ContactsContentObserver mObserver;
-
- public ContactsManager(final Context context) {
- mContext = context;
- mObserver = new ContactsContentObserver(this /* ContactsManager */, context);
- }
-
- // TODO: This was synchronized in previous version. Why?
- public void registerForUpdates(final ContactsChangedListener listener) {
- mObserver.registerObserver(listener);
- }
-
- public int getContactCountAtLastRebuild() {
- return mContactCountAtLastRebuild.get();
- }
-
- public int getHashCodeAtLastRebuild() {
- return mHashCodeAtLastRebuild.get();
- }
-
- /**
- * Returns all the valid names in the Contacts DB. Callers should also
- * call {@link #updateLocalState(ArrayList)} after they are done with result
- * so that the manager can cache local state for determining updates.
- *
- * These names are sorted by their affinity to the user, with favorite
- * contacts appearing first.
- */
- public ArrayList<String> getValidNames(final Uri uri) {
- // Check all contacts since it's not possible to find out which names have changed.
- // This is needed because it's possible to receive extraneous onChange events even when no
- // name has changed.
- final Cursor cursor = mContext.getContentResolver().query(uri,
- ContactsDictionaryConstants.PROJECTION, null, null, null);
- final ArrayList<RankedContact> contacts = new ArrayList<>();
- int maxTimesContacted = 0;
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast()) {
- final String name = cursor.getString(
- ContactsDictionaryConstants.NAME_INDEX);
- if (isValidName(name)) {
- final int timesContacted = cursor.getInt(
- ContactsDictionaryConstants.TIMES_CONTACTED_INDEX);
- if (timesContacted > maxTimesContacted) {
- maxTimesContacted = timesContacted;
- }
- contacts.add(new RankedContact(cursor));
- }
- cursor.moveToNext();
- }
- }
- } finally {
- cursor.close();
- }
- }
- final long currentTime = System.currentTimeMillis();
- for (RankedContact contact : contacts) {
- contact.computeAffinity(maxTimesContacted, currentTime);
- }
- Collections.sort(contacts, new AffinityComparator());
- final HashSet<String> names = new HashSet<>();
- for (int i = 0; i < contacts.size() && names.size() < MAX_CONTACT_NAMES; ++i) {
- names.add(contacts.get(i).mName);
- }
- return new ArrayList<>(names);
- }
-
- /**
- * Returns the number of contacts in contacts content provider.
- */
- public int getContactCount() {
- // TODO: consider switching to a rawQuery("select count(*)...") on the database if
- // performance is a bottleneck.
- Cursor cursor = null;
- try {
- cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI,
- ContactsDictionaryConstants.PROJECTION_ID_ONLY, null, null, null);
- if (null == cursor) {
- return 0;
- }
- return cursor.getCount();
- } catch (final SQLiteException e) {
- Log.e(TAG, "SQLiteException in the remote Contacts process.", e);
- } finally {
- if (null != cursor) {
- cursor.close();
- }
- }
- return 0;
- }
-
- private static boolean isValidName(final String name) {
- if (TextUtils.isEmpty(name) || name.indexOf(Constants.CODE_COMMERCIAL_AT) != -1) {
- return false;
- }
- final boolean hasSpace = name.indexOf(Constants.CODE_SPACE) != -1;
- if (!hasSpace) {
- // Only allow an isolated word if it does not contain a hyphen.
- // This helps to filter out mailing lists.
- return name.indexOf(Constants.CODE_DASH) == -1;
- }
- return true;
- }
-
- /**
- * Updates the local state of the manager. This should be called when the callers
- * are done with all the updates of the content provider successfully.
- */
- public void updateLocalState(final ArrayList<String> names) {
- mContactCountAtLastRebuild.set(getContactCount());
- mHashCodeAtLastRebuild.set(names.hashCode());
- }
-
- /**
- * Performs any necessary cleanup.
- */
- public void close() {
- mObserver.unregister();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
deleted file mode 100644
index 6816f129a..000000000
--- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.common.NativeSuggestOptions;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.utils.JniUtils;
-
-import java.util.Locale;
-
-public final class DicTraverseSession {
- static {
- JniUtils.loadNativeLibrary();
- }
- // Must be equal to MAX_RESULTS in native/jni/src/defines.h
- private static final int MAX_RESULTS = 18;
- public final int[] mInputCodePoints =
- new int[DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH];
- public final int[][] mPrevWordCodePointArrays =
- new int[DecoderSpecificConstants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][];
- public final boolean[] mIsBeginningOfSentenceArray =
- new boolean[DecoderSpecificConstants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
- public final int[] mOutputSuggestionCount = new int[1];
- public final int[] mOutputCodePoints =
- new int[DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH * MAX_RESULTS];
- public final int[] mSpaceIndices = new int[MAX_RESULTS];
- public final int[] mOutputScores = new int[MAX_RESULTS];
- public final int[] mOutputTypes = new int[MAX_RESULTS];
- // Only one result is ever used
- public final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
- public final float[] mInputOutputWeightOfLangModelVsSpatialModel = new float[1];
-
- public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
-
- private static native long setDicTraverseSessionNative(String locale, long dictSize);
- private static native void initDicTraverseSessionNative(long nativeDicTraverseSession,
- long dictionary, int[] previousWord, int previousWordLength);
- private static native void releaseDicTraverseSessionNative(long nativeDicTraverseSession);
-
- private long mNativeDicTraverseSession;
-
- public DicTraverseSession(Locale locale, long dictionary, long dictSize) {
- mNativeDicTraverseSession = createNativeDicTraverseSession(
- locale != null ? locale.toString() : "", dictSize);
- initSession(dictionary);
- }
-
- public long getSession() {
- return mNativeDicTraverseSession;
- }
-
- public void initSession(long dictionary) {
- initSession(dictionary, null, 0);
- }
-
- public void initSession(long dictionary, int[] previousWord, int previousWordLength) {
- initDicTraverseSessionNative(
- mNativeDicTraverseSession, dictionary, previousWord, previousWordLength);
- }
-
- private static long createNativeDicTraverseSession(String locale, long dictSize) {
- return setDicTraverseSessionNative(locale, dictSize);
- }
-
- private void closeInternal() {
- if (mNativeDicTraverseSession != 0) {
- releaseDicTraverseSessionNative(mNativeDicTraverseSession);
- mNativeDicTraverseSession = 0;
- }
- }
-
- public void close() {
- closeInternal();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- closeInternal();
- } finally {
- super.finalize();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
deleted file mode 100644
index e00219c98..000000000
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.Arrays;
-import java.util.HashSet;
-
-/**
- * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
- * strokes.
- */
-public abstract class Dictionary {
- public static final int NOT_A_PROBABILITY = -1;
- public static final float NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL = -1.0f;
-
- // The following types do not actually come from real dictionary instances, so we create
- // corresponding instances.
- public static final String TYPE_USER_TYPED = "user_typed";
- public static final PhonyDictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED);
-
- public static final String TYPE_USER_SHORTCUT = "user_shortcut";
- public static final PhonyDictionary DICTIONARY_USER_SHORTCUT =
- new PhonyDictionary(TYPE_USER_SHORTCUT);
-
- public static final String TYPE_APPLICATION_DEFINED = "application_defined";
- public static final PhonyDictionary DICTIONARY_APPLICATION_DEFINED =
- new PhonyDictionary(TYPE_APPLICATION_DEFINED);
-
- public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such
- public static final PhonyDictionary DICTIONARY_HARDCODED =
- new PhonyDictionary(TYPE_HARDCODED);
-
- // Spawned by resuming suggestions. Comes from a span that was in the TextView.
- public static final String TYPE_RESUMED = "resumed";
- public static final PhonyDictionary DICTIONARY_RESUMED = new PhonyDictionary(TYPE_RESUMED);
-
- // The following types of dictionary have actual functional instances. We don't need final
- // phony dictionary instances for them.
- public static final String TYPE_MAIN = "main";
- public static final String TYPE_CONTACTS = "contacts";
- // User dictionary, the system-managed one.
- public static final String TYPE_USER = "user";
- // User history dictionary internal to LatinIME.
- public static final String TYPE_USER_HISTORY = "history";
- public final String mDictType;
- // The locale for this dictionary. May be null if unknown (phony dictionary for example).
- public final Locale mLocale;
-
- /**
- * Set out of the dictionary types listed above that are based on data specific to the user,
- * e.g., the user's contacts.
- */
- private static final HashSet<String> sUserSpecificDictionaryTypes = new HashSet<>(Arrays.asList(
- TYPE_USER_TYPED,
- TYPE_USER,
- TYPE_CONTACTS,
- TYPE_USER_HISTORY));
-
- public Dictionary(final String dictType, final Locale locale) {
- mDictType = dictType;
- mLocale = locale;
- }
-
- /**
- * Searches for suggestions for a given context.
- * @param composedData the key sequence to match with coordinate info
- * @param ngramContext the context for n-gram.
- * @param proximityInfoHandle the handle for key proximity. Is ignored by some implementations.
- * @param settingsValuesForSuggestion the settings values used for the suggestion.
- * @param sessionId the session id.
- * @param weightForLocale the weight given to this locale, to multiply the output scores for
- * multilingual input.
- * @param inOutWeightOfLangModelVsSpatialModel the weight of the language model as a ratio of
- * the spatial model, used for generating suggestions. inOutWeightOfLangModelVsSpatialModel is
- * a float array that has only one element. This can be updated when a different value is used.
- * @return the list of suggestions (possibly null if none)
- */
- abstract public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
- final NgramContext ngramContext, final long proximityInfoHandle,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int sessionId, final float weightForLocale,
- final float[] inOutWeightOfLangModelVsSpatialModel);
-
- /**
- * Checks if the given word has to be treated as a valid word. Please note that some
- * dictionaries have entries that should be treated as invalid words.
- * @param word the word to search for. The search should be case-insensitive.
- * @return true if the word is valid, false otherwise
- */
- public boolean isValidWord(final String word) {
- return isInDictionary(word);
- }
-
- /**
- * Checks if the given word is in the dictionary regardless of it being valid or not.
- */
- abstract public boolean isInDictionary(final String word);
-
- /**
- * Get the frequency of the word.
- * @param word the word to get the frequency of.
- */
- public int getFrequency(final String word) {
- return NOT_A_PROBABILITY;
- }
-
- /**
- * Get the maximum frequency of the word.
- * @param word the word to get the maximum frequency of.
- */
- public int getMaxFrequencyOfExactMatches(final String word) {
- return NOT_A_PROBABILITY;
- }
-
- /**
- * Compares the contents of the character array with the typed word and returns true if they
- * are the same.
- * @param word the array of characters that make up the word
- * @param length the number of valid characters in the character array
- * @param typedWord the word to compare with
- * @return true if they are the same, false otherwise.
- */
- protected boolean same(final char[] word, final int length, final String typedWord) {
- if (typedWord.length() != length) {
- return false;
- }
- for (int i = 0; i < length; i++) {
- if (word[i] != typedWord.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Override to clean up any resources.
- */
- public void close() {
- // empty base implementation
- }
-
- /**
- * Subclasses may override to indicate that this Dictionary is not yet properly initialized.
- */
- public boolean isInitialized() {
- return true;
- }
-
- /**
- * Whether we think this suggestion should trigger an auto-commit. prevWord is the word
- * before the suggestion, so that we can use n-gram frequencies.
- * @param candidate The candidate suggestion, in whole (not only the first part).
- * @return whether we should auto-commit or not.
- */
- public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
- // If we don't have support for auto-commit, or if we don't know, we return false to
- // avoid auto-committing stuff. Implementations of the Dictionary class that know to
- // determine whether we should auto-commit will override this.
- return false;
- }
-
- /**
- * Whether this dictionary is based on data specific to the user, e.g., the user's contacts.
- * @return Whether this dictionary is specific to the user.
- */
- public boolean isUserSpecific() {
- return sUserSpecificDictionaryTypes.contains(mDictType);
- }
-
- /**
- * Not a true dictionary. A placeholder used to indicate suggestions that don't come from any
- * real dictionary.
- */
- @UsedForTesting
- static class PhonyDictionary extends Dictionary {
- @UsedForTesting
- PhonyDictionary(final String type) {
- super(type, null);
- }
-
- @Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
- final NgramContext ngramContext, final long proximityInfoHandle,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int sessionId, final float weightForLocale,
- final float[] inOutWeightOfLangModelVsSpatialModel) {
- return null;
- }
-
- @Override
- public boolean isInDictionary(String word) {
- return false;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
deleted file mode 100644
index 96575f629..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.util.Log;
-
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Class for a collection of dictionaries that behave like one dictionary.
- */
-public final class DictionaryCollection extends Dictionary {
- private final String TAG = DictionaryCollection.class.getSimpleName();
- protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
-
- public DictionaryCollection(final String dictType, final Locale locale) {
- super(dictType, locale);
- mDictionaries = new CopyOnWriteArrayList<>();
- }
-
- public DictionaryCollection(final String dictType, final Locale locale,
- final Dictionary... dictionaries) {
- super(dictType, locale);
- if (null == dictionaries) {
- mDictionaries = new CopyOnWriteArrayList<>();
- } else {
- mDictionaries = new CopyOnWriteArrayList<>(dictionaries);
- mDictionaries.removeAll(Collections.singleton(null));
- }
- }
-
- public DictionaryCollection(final String dictType, final Locale locale,
- final Collection<Dictionary> dictionaries) {
- super(dictType, locale);
- mDictionaries = new CopyOnWriteArrayList<>(dictionaries);
- mDictionaries.removeAll(Collections.singleton(null));
- }
-
- @Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
- final NgramContext ngramContext, final long proximityInfoHandle,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int sessionId, final float weightForLocale,
- final float[] inOutWeightOfLangModelVsSpatialModel) {
- final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
- if (dictionaries.isEmpty()) return null;
- // To avoid creating unnecessary objects, we get the list out of the first
- // dictionary and add the rest to it if not null, hence the get(0)
- ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composedData,
- ngramContext, proximityInfoHandle, settingsValuesForSuggestion, sessionId,
- weightForLocale, inOutWeightOfLangModelVsSpatialModel);
- if (null == suggestions) suggestions = new ArrayList<>();
- final int length = dictionaries.size();
- for (int i = 1; i < length; ++ i) {
- final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(
- composedData, ngramContext, proximityInfoHandle, settingsValuesForSuggestion,
- sessionId, weightForLocale, inOutWeightOfLangModelVsSpatialModel);
- if (null != sugg) suggestions.addAll(sugg);
- }
- return suggestions;
- }
-
- @Override
- public boolean isInDictionary(final String word) {
- for (int i = mDictionaries.size() - 1; i >= 0; --i)
- if (mDictionaries.get(i).isInDictionary(word)) return true;
- return false;
- }
-
- @Override
- public int getFrequency(final String word) {
- int maxFreq = -1;
- for (int i = mDictionaries.size() - 1; i >= 0; --i) {
- final int tempFreq = mDictionaries.get(i).getFrequency(word);
- maxFreq = Math.max(tempFreq, maxFreq);
- }
- return maxFreq;
- }
-
- @Override
- public int getMaxFrequencyOfExactMatches(final String word) {
- int maxFreq = -1;
- for (int i = mDictionaries.size() - 1; i >= 0; --i) {
- final int tempFreq = mDictionaries.get(i).getMaxFrequencyOfExactMatches(word);
- maxFreq = Math.max(tempFreq, maxFreq);
- }
- return maxFreq;
- }
-
- @Override
- public boolean isInitialized() {
- return !mDictionaries.isEmpty();
- }
-
- @Override
- public void close() {
- for (final Dictionary dict : mDictionaries)
- dict.close();
- }
-
- // Warning: this is not thread-safe. Take necessary precaution when calling.
- public void addDictionary(final Dictionary newDict) {
- if (null == newDict) return;
- if (mDictionaries.contains(newDict)) {
- Log.w(TAG, "This collection already contains this dictionary: " + newDict);
- }
- mDictionaries.add(newDict);
- }
-
- // Warning: this is not thread-safe. Take necessary precaution when calling.
- public void removeDictionary(final Dictionary dict) {
- if (mDictionaries.contains(dict)) {
- mDictionaries.remove(dict);
- } else {
- Log.w(TAG, "This collection does not contain this dictionary: " + dict);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java
deleted file mode 100644
index ee2fdc6c7..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-public class DictionaryDumpBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = DictionaryDumpBroadcastReceiver.class.getSimpleName();
-
- private static final String DOMAIN = "com.android.inputmethod.latin";
- public static final String DICTIONARY_DUMP_INTENT_ACTION = DOMAIN + ".DICT_DUMP";
- public static final String DICTIONARY_NAME_KEY = "dictName";
-
- final LatinIME mLatinIme;
-
- public DictionaryDumpBroadcastReceiver(final LatinIME latinIme) {
- mLatinIme = latinIme;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(DICTIONARY_DUMP_INTENT_ACTION)) {
- final String dictName = intent.getStringExtra(DICTIONARY_NAME_KEY);
- if (dictName == null) {
- Log.e(TAG, "Received dictionary dump intent action " +
- "but the dictionary name is not set.");
- return;
- }
- mLatinIme.dumpDictionaryForDebug(dictName);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
deleted file mode 100644
index 02015da09..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.util.LruCache;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.utils.SuggestionResults;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Interface that facilitates interaction with different kinds of dictionaries. Provides APIs to
- * instantiate and select the correct dictionaries (based on language or account), update entries
- * and fetch suggestions. Currently AndroidSpellCheckerService and LatinIME both use
- * DictionaryFacilitator as a client for interacting with dictionaries.
- */
-public interface DictionaryFacilitator {
-
- public static final String[] ALL_DICTIONARY_TYPES = new String[] {
- Dictionary.TYPE_MAIN,
- Dictionary.TYPE_CONTACTS,
- Dictionary.TYPE_USER_HISTORY,
- Dictionary.TYPE_USER};
-
- public static final String[] DYNAMIC_DICTIONARY_TYPES = new String[] {
- Dictionary.TYPE_CONTACTS,
- Dictionary.TYPE_USER_HISTORY,
- Dictionary.TYPE_USER};
-
- /**
- * The facilitator will put words into the cache whenever it decodes them.
- * @param cache
- */
- void setValidSpellingWordReadCache(final LruCache<String, Boolean> cache);
-
- /**
- * The facilitator will get words from the cache whenever it needs to check their spelling.
- * @param cache
- */
- void setValidSpellingWordWriteCache(final LruCache<String, Boolean> cache);
-
- /**
- * Returns whether this facilitator is exactly for this locale.
- *
- * @param locale the locale to test against
- */
- boolean isForLocale(final Locale locale);
-
- /**
- * Returns whether this facilitator is exactly for this account.
- *
- * @param account the account to test against.
- */
- boolean isForAccount(@Nullable final String account);
-
- interface DictionaryInitializationListener {
- void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
- }
-
- /**
- * Called every time {@link LatinIME} starts on a new text field.
- * Dot not affect {@link AndroidSpellCheckerService}.
- *
- * WARNING: The service methods that call start/finish are very spammy.
- */
- void onStartInput();
-
- /**
- * Called every time the {@link LatinIME} finishes with the current text field.
- * May be followed by {@link #onStartInput} again in another text field,
- * or it may be done for a while.
- * Dot not affect {@link AndroidSpellCheckerService}.
- *
- * WARNING: The service methods that call start/finish are very spammy.
- */
- void onFinishInput(Context context);
-
- boolean isActive();
-
- Locale getLocale();
-
- boolean usesContacts();
-
- String getAccount();
-
- void resetDictionaries(
- final Context context,
- final Locale newLocale,
- final boolean useContactsDict,
- final boolean usePersonalizedDicts,
- final boolean forceReloadMainDictionary,
- @Nullable final String account,
- final String dictNamePrefix,
- @Nullable final DictionaryInitializationListener listener);
-
- @UsedForTesting
- void resetDictionariesForTesting(
- final Context context,
- final Locale locale,
- final ArrayList<String> dictionaryTypes,
- final HashMap<String, File> dictionaryFiles,
- final Map<String, Map<String, String>> additionalDictAttributes,
- @Nullable final String account);
-
- void closeDictionaries();
-
- @UsedForTesting
- ExpandableBinaryDictionary getSubDictForTesting(final String dictName);
-
- // The main dictionaries are loaded asynchronously. Don't cache the return value
- // of these methods.
- boolean hasAtLeastOneInitializedMainDictionary();
-
- boolean hasAtLeastOneUninitializedMainDictionary();
-
- void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit)
- throws InterruptedException;
-
- @UsedForTesting
- void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit)
- throws InterruptedException;
-
- void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized,
- @Nonnull final NgramContext ngramContext, final long timeStampInSeconds,
- final boolean blockPotentiallyOffensive);
-
- void unlearnFromUserHistory(final String word,
- @Nonnull final NgramContext ngramContext, final long timeStampInSeconds,
- final int eventType);
-
- // TODO: Revise the way to fusion suggestion results.
- @Nonnull SuggestionResults getSuggestionResults(final ComposedData composedData,
- final NgramContext ngramContext, @Nonnull final Keyboard keyboard,
- final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId,
- final int inputStyle);
-
- boolean isValidSpellingWord(final String word);
-
- boolean isValidSuggestionWord(final String word);
-
- boolean clearUserHistoryDictionary(final Context context);
-
- String dump(final Context context);
-
- void dumpDictionaryForDebug(final String dictName);
-
- @Nonnull List<DictionaryStats> getDictionaryStats(final Context context);
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java
deleted file mode 100644
index b435de867..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java
+++ /dev/null
@@ -1,736 +0,0 @@
-/*
-7 * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.Manifest;
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.LruCache;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.NgramContext.WordInfo;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.utils.ExecutorUtils;
-import com.android.inputmethod.latin.utils.SuggestionResults;
-
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Facilitates interaction with different kinds of dictionaries. Provides APIs
- * to instantiate and select the correct dictionaries (based on language or account),
- * update entries and fetch suggestions.
- *
- * Currently AndroidSpellCheckerService and LatinIME both use DictionaryFacilitator as
- * a client for interacting with dictionaries.
- */
-public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
- // TODO: Consolidate dictionaries in native code.
- public static final String TAG = DictionaryFacilitatorImpl.class.getSimpleName();
-
- // HACK: This threshold is being used when adding a capitalized entry in the User History
- // dictionary.
- private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140;
-
- private DictionaryGroup mDictionaryGroup = new DictionaryGroup();
- private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0);
- // To synchronize assigning mDictionaryGroup to ensure closing dictionaries.
- private final Object mLock = new Object();
-
- public static final Map<String, Class<? extends ExpandableBinaryDictionary>>
- DICT_TYPE_TO_CLASS = new HashMap<>();
-
- static {
- DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER_HISTORY, UserHistoryDictionary.class);
- DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER, UserBinaryDictionary.class);
- DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_CONTACTS, ContactsBinaryDictionary.class);
- }
-
- private static final String DICT_FACTORY_METHOD_NAME = "getDictionary";
- private static final Class<?>[] DICT_FACTORY_METHOD_ARG_TYPES =
- new Class[] { Context.class, Locale.class, File.class, String.class, String.class };
-
- private LruCache<String, Boolean> mValidSpellingWordReadCache;
- private LruCache<String, Boolean> mValidSpellingWordWriteCache;
-
- @Override
- public void setValidSpellingWordReadCache(final LruCache<String, Boolean> cache) {
- mValidSpellingWordReadCache = cache;
- }
-
- @Override
- public void setValidSpellingWordWriteCache(final LruCache<String, Boolean> cache) {
- mValidSpellingWordWriteCache = cache;
- }
-
- @Override
- public boolean isForLocale(final Locale locale) {
- return locale != null && locale.equals(mDictionaryGroup.mLocale);
- }
-
- /**
- * Returns whether this facilitator is exactly for this account.
- *
- * @param account the account to test against.
- */
- public boolean isForAccount(@Nullable final String account) {
- return TextUtils.equals(mDictionaryGroup.mAccount, account);
- }
-
- /**
- * A group of dictionaries that work together for a single language.
- */
- private static class DictionaryGroup {
- // TODO: Add null analysis annotations.
- // TODO: Run evaluation to determine a reasonable value for these constants. The current
- // values are ad-hoc and chosen without any particular care or methodology.
- public static final float WEIGHT_FOR_MOST_PROBABLE_LANGUAGE = 1.0f;
- public static final float WEIGHT_FOR_GESTURING_IN_NOT_MOST_PROBABLE_LANGUAGE = 0.95f;
- public static final float WEIGHT_FOR_TYPING_IN_NOT_MOST_PROBABLE_LANGUAGE = 0.6f;
-
- /**
- * The locale associated with the dictionary group.
- */
- @Nullable public final Locale mLocale;
-
- /**
- * The user account associated with the dictionary group.
- */
- @Nullable public final String mAccount;
-
- @Nullable private Dictionary mMainDict;
- // Confidence that the most probable language is actually the language the user is
- // typing in. For now, this is simply the number of times a word from this language
- // has been committed in a row.
- private int mConfidence = 0;
-
- public float mWeightForTypingInLocale = WEIGHT_FOR_MOST_PROBABLE_LANGUAGE;
- public float mWeightForGesturingInLocale = WEIGHT_FOR_MOST_PROBABLE_LANGUAGE;
- public final ConcurrentHashMap<String, ExpandableBinaryDictionary> mSubDictMap =
- new ConcurrentHashMap<>();
-
- public DictionaryGroup() {
- this(null /* locale */, null /* mainDict */, null /* account */,
- Collections.<String, ExpandableBinaryDictionary>emptyMap() /* subDicts */);
- }
-
- public DictionaryGroup(@Nullable final Locale locale,
- @Nullable final Dictionary mainDict,
- @Nullable final String account,
- final Map<String, ExpandableBinaryDictionary> subDicts) {
- mLocale = locale;
- mAccount = account;
- // The main dictionary can be asynchronously loaded.
- setMainDict(mainDict);
- for (final Map.Entry<String, ExpandableBinaryDictionary> entry : subDicts.entrySet()) {
- setSubDict(entry.getKey(), entry.getValue());
- }
- }
-
- private void setSubDict(final String dictType, final ExpandableBinaryDictionary dict) {
- if (dict != null) {
- mSubDictMap.put(dictType, dict);
- }
- }
-
- public void setMainDict(final Dictionary mainDict) {
- // Close old dictionary if exists. Main dictionary can be assigned multiple times.
- final Dictionary oldDict = mMainDict;
- mMainDict = mainDict;
- if (oldDict != null && mainDict != oldDict) {
- oldDict.close();
- }
- }
-
- public Dictionary getDict(final String dictType) {
- if (Dictionary.TYPE_MAIN.equals(dictType)) {
- return mMainDict;
- }
- return getSubDict(dictType);
- }
-
- public ExpandableBinaryDictionary getSubDict(final String dictType) {
- return mSubDictMap.get(dictType);
- }
-
- public boolean hasDict(final String dictType, @Nullable final String account) {
- if (Dictionary.TYPE_MAIN.equals(dictType)) {
- return mMainDict != null;
- }
- if (Dictionary.TYPE_USER_HISTORY.equals(dictType) &&
- !TextUtils.equals(account, mAccount)) {
- // If the dictionary type is user history, & if the account doesn't match,
- // return immediately. If the account matches, continue looking it up in the
- // sub dictionary map.
- return false;
- }
- return mSubDictMap.containsKey(dictType);
- }
-
- public void closeDict(final String dictType) {
- final Dictionary dict;
- if (Dictionary.TYPE_MAIN.equals(dictType)) {
- dict = mMainDict;
- } else {
- dict = mSubDictMap.remove(dictType);
- }
- if (dict != null) {
- dict.close();
- }
- }
- }
-
- public DictionaryFacilitatorImpl() {
- }
-
- @Override
- public void onStartInput() {
- }
-
- @Override
- public void onFinishInput(Context context) {
- }
-
- @Override
- public boolean isActive() {
- return mDictionaryGroup.mLocale != null;
- }
-
- @Override
- public Locale getLocale() {
- return mDictionaryGroup.mLocale;
- }
-
- @Override
- public boolean usesContacts() {
- return mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTACTS) != null;
- }
-
- @Override
- public String getAccount() {
- return null;
- }
-
- @Nullable
- private static ExpandableBinaryDictionary getSubDict(final String dictType,
- final Context context, final Locale locale, final File dictFile,
- final String dictNamePrefix, @Nullable final String account) {
- final Class<? extends ExpandableBinaryDictionary> dictClass =
- DICT_TYPE_TO_CLASS.get(dictType);
- if (dictClass == null) {
- return null;
- }
- try {
- final Method factoryMethod = dictClass.getMethod(DICT_FACTORY_METHOD_NAME,
- DICT_FACTORY_METHOD_ARG_TYPES);
- final Object dict = factoryMethod.invoke(null /* obj */,
- new Object[] { context, locale, dictFile, dictNamePrefix, account });
- return (ExpandableBinaryDictionary) dict;
- } catch (final NoSuchMethodException | SecurityException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException e) {
- Log.e(TAG, "Cannot create dictionary: " + dictType, e);
- return null;
- }
- }
-
- @Nullable
- static DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup dictionaryGroup,
- final Locale locale) {
- return locale.equals(dictionaryGroup.mLocale) ? dictionaryGroup : null;
- }
-
- @Override
- public void resetDictionaries(
- final Context context,
- final Locale newLocale,
- final boolean useContactsDict,
- final boolean usePersonalizedDicts,
- final boolean forceReloadMainDictionary,
- @Nullable final String account,
- final String dictNamePrefix,
- @Nullable final DictionaryInitializationListener listener) {
- final HashMap<Locale, ArrayList<String>> existingDictionariesToCleanup = new HashMap<>();
- // TODO: Make subDictTypesToUse configurable by resource or a static final list.
- final HashSet<String> subDictTypesToUse = new HashSet<>();
- subDictTypesToUse.add(Dictionary.TYPE_USER);
-
- // Do not use contacts dictionary if we do not have permissions to read contacts.
- final boolean contactsPermissionGranted = PermissionsUtil.checkAllPermissionsGranted(
- context, Manifest.permission.READ_CONTACTS);
- if (useContactsDict && contactsPermissionGranted) {
- subDictTypesToUse.add(Dictionary.TYPE_CONTACTS);
- }
- if (usePersonalizedDicts) {
- subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY);
- }
-
- // Gather all dictionaries. We'll remove them from the list to clean up later.
- final ArrayList<String> dictTypeForLocale = new ArrayList<>();
- existingDictionariesToCleanup.put(newLocale, dictTypeForLocale);
- final DictionaryGroup currentDictionaryGroupForLocale =
- findDictionaryGroupWithLocale(mDictionaryGroup, newLocale);
- if (currentDictionaryGroupForLocale != null) {
- for (final String dictType : DYNAMIC_DICTIONARY_TYPES) {
- if (currentDictionaryGroupForLocale.hasDict(dictType, account)) {
- dictTypeForLocale.add(dictType);
- }
- }
- if (currentDictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN, account)) {
- dictTypeForLocale.add(Dictionary.TYPE_MAIN);
- }
- }
-
- final DictionaryGroup dictionaryGroupForLocale =
- findDictionaryGroupWithLocale(mDictionaryGroup, newLocale);
- final ArrayList<String> dictTypesToCleanupForLocale =
- existingDictionariesToCleanup.get(newLocale);
- final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale);
-
- final Dictionary mainDict;
- if (forceReloadMainDictionary || noExistingDictsForThisLocale
- || !dictionaryGroupForLocale.hasDict(Dictionary.TYPE_MAIN, account)) {
- mainDict = null;
- } else {
- mainDict = dictionaryGroupForLocale.getDict(Dictionary.TYPE_MAIN);
- dictTypesToCleanupForLocale.remove(Dictionary.TYPE_MAIN);
- }
-
- final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>();
- for (final String subDictType : subDictTypesToUse) {
- final ExpandableBinaryDictionary subDict;
- if (noExistingDictsForThisLocale
- || !dictionaryGroupForLocale.hasDict(subDictType, account)) {
- // Create a new dictionary.
- subDict = getSubDict(subDictType, context, newLocale, null /* dictFile */,
- dictNamePrefix, account);
- } else {
- // Reuse the existing dictionary, and don't close it at the end
- subDict = dictionaryGroupForLocale.getSubDict(subDictType);
- dictTypesToCleanupForLocale.remove(subDictType);
- }
- subDicts.put(subDictType, subDict);
- }
- DictionaryGroup newDictionaryGroup =
- new DictionaryGroup(newLocale, mainDict, account, subDicts);
-
- // Replace Dictionaries.
- final DictionaryGroup oldDictionaryGroup;
- synchronized (mLock) {
- oldDictionaryGroup = mDictionaryGroup;
- mDictionaryGroup = newDictionaryGroup;
- if (hasAtLeastOneUninitializedMainDictionary()) {
- asyncReloadUninitializedMainDictionaries(context, newLocale, listener);
- }
- }
- if (listener != null) {
- listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary());
- }
-
- // Clean up old dictionaries.
- for (final Locale localeToCleanUp : existingDictionariesToCleanup.keySet()) {
- final ArrayList<String> dictTypesToCleanUp =
- existingDictionariesToCleanup.get(localeToCleanUp);
- final DictionaryGroup dictionarySetToCleanup =
- findDictionaryGroupWithLocale(oldDictionaryGroup, localeToCleanUp);
- for (final String dictType : dictTypesToCleanUp) {
- dictionarySetToCleanup.closeDict(dictType);
- }
- }
-
- if (mValidSpellingWordWriteCache != null) {
- mValidSpellingWordWriteCache.evictAll();
- }
- }
-
- private void asyncReloadUninitializedMainDictionaries(final Context context,
- final Locale locale, final DictionaryInitializationListener listener) {
- final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1);
- mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary;
- ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() {
- @Override
- public void run() {
- doReloadUninitializedMainDictionaries(
- context, locale, listener, latchForWaitingLoadingMainDictionary);
- }
- });
- }
-
- void doReloadUninitializedMainDictionaries(final Context context, final Locale locale,
- final DictionaryInitializationListener listener,
- final CountDownLatch latchForWaitingLoadingMainDictionary) {
- final DictionaryGroup dictionaryGroup =
- findDictionaryGroupWithLocale(mDictionaryGroup, locale);
- if (null == dictionaryGroup) {
- // This should never happen, but better safe than crashy
- Log.w(TAG, "Expected a dictionary group for " + locale + " but none found");
- return;
- }
- final Dictionary mainDict =
- DictionaryFactory.createMainDictionaryFromManager(context, locale);
- synchronized (mLock) {
- if (locale.equals(dictionaryGroup.mLocale)) {
- dictionaryGroup.setMainDict(mainDict);
- } else {
- // Dictionary facilitator has been reset for another locale.
- mainDict.close();
- }
- }
- if (listener != null) {
- listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary());
- }
- latchForWaitingLoadingMainDictionary.countDown();
- }
-
- @UsedForTesting
- public void resetDictionariesForTesting(final Context context, final Locale locale,
- final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles,
- final Map<String, Map<String, String>> additionalDictAttributes,
- @Nullable final String account) {
- Dictionary mainDictionary = null;
- final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>();
-
- for (final String dictType : dictionaryTypes) {
- if (dictType.equals(Dictionary.TYPE_MAIN)) {
- mainDictionary = DictionaryFactory.createMainDictionaryFromManager(context,
- locale);
- } else {
- final File dictFile = dictionaryFiles.get(dictType);
- final ExpandableBinaryDictionary dict = getSubDict(
- dictType, context, locale, dictFile, "" /* dictNamePrefix */, account);
- if (additionalDictAttributes.containsKey(dictType)) {
- dict.clearAndFlushDictionaryWithAdditionalAttributes(
- additionalDictAttributes.get(dictType));
- }
- if (dict == null) {
- throw new RuntimeException("Unknown dictionary type: " + dictType);
- }
- dict.reloadDictionaryIfRequired();
- dict.waitAllTasksForTests();
- subDicts.put(dictType, dict);
- }
- }
- mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, account, subDicts);
- }
-
- public void closeDictionaries() {
- final DictionaryGroup dictionaryGroupToClose;
- synchronized (mLock) {
- dictionaryGroupToClose = mDictionaryGroup;
- mDictionaryGroup = new DictionaryGroup();
- }
- for (final String dictType : ALL_DICTIONARY_TYPES) {
- dictionaryGroupToClose.closeDict(dictType);
- }
- }
-
- @UsedForTesting
- public ExpandableBinaryDictionary getSubDictForTesting(final String dictName) {
- return mDictionaryGroup.getSubDict(dictName);
- }
-
- // The main dictionaries are loaded asynchronously. Don't cache the return value
- // of these methods.
- public boolean hasAtLeastOneInitializedMainDictionary() {
- final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN);
- if (mainDict != null && mainDict.isInitialized()) {
- return true;
- }
- return false;
- }
-
- public boolean hasAtLeastOneUninitializedMainDictionary() {
- final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN);
- if (mainDict == null || !mainDict.isInitialized()) {
- return true;
- }
- return false;
- }
-
- public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit)
- throws InterruptedException {
- mLatchForWaitingLoadingMainDictionaries.await(timeout, unit);
- }
-
- @UsedForTesting
- public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit)
- throws InterruptedException {
- waitForLoadingMainDictionaries(timeout, unit);
- for (final ExpandableBinaryDictionary dict : mDictionaryGroup.mSubDictMap.values()) {
- dict.waitAllTasksForTests();
- }
- }
-
- public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized,
- @Nonnull final NgramContext ngramContext, final long timeStampInSeconds,
- final boolean blockPotentiallyOffensive) {
- // Update the spelling cache before learning. Words that are not yet added to user history
- // and appear in no other language model are not considered valid.
- putWordIntoValidSpellingWordCache("addToUserHistory", suggestion);
-
- final String[] words = suggestion.split(Constants.WORD_SEPARATOR);
- NgramContext ngramContextForCurrentWord = ngramContext;
- for (int i = 0; i < words.length; i++) {
- final String currentWord = words[i];
- final boolean wasCurrentWordAutoCapitalized = (i == 0) ? wasAutoCapitalized : false;
- addWordToUserHistory(mDictionaryGroup, ngramContextForCurrentWord, currentWord,
- wasCurrentWordAutoCapitalized, (int) timeStampInSeconds,
- blockPotentiallyOffensive);
- ngramContextForCurrentWord =
- ngramContextForCurrentWord.getNextNgramContext(new WordInfo(currentWord));
- }
- }
-
- private void putWordIntoValidSpellingWordCache(
- @Nonnull final String caller,
- @Nonnull final String originalWord) {
- if (mValidSpellingWordWriteCache == null) {
- return;
- }
-
- final String lowerCaseWord = originalWord.toLowerCase(getLocale());
- final boolean lowerCaseValid = isValidSpellingWord(lowerCaseWord);
- mValidSpellingWordWriteCache.put(lowerCaseWord, lowerCaseValid);
-
- final String capitalWord =
- StringUtils.capitalizeFirstAndDowncaseRest(originalWord, getLocale());
- final boolean capitalValid;
- if (lowerCaseValid) {
- // The lower case form of the word is valid, so the upper case must be valid.
- capitalValid = true;
- } else {
- capitalValid = isValidSpellingWord(capitalWord);
- }
- mValidSpellingWordWriteCache.put(capitalWord, capitalValid);
- }
-
- private void addWordToUserHistory(final DictionaryGroup dictionaryGroup,
- final NgramContext ngramContext, final String word, final boolean wasAutoCapitalized,
- final int timeStampInSeconds, final boolean blockPotentiallyOffensive) {
- final ExpandableBinaryDictionary userHistoryDictionary =
- dictionaryGroup.getSubDict(Dictionary.TYPE_USER_HISTORY);
- if (userHistoryDictionary == null || !isForLocale(userHistoryDictionary.mLocale)) {
- return;
- }
- final int maxFreq = getFrequency(word);
- if (maxFreq == 0 && blockPotentiallyOffensive) {
- return;
- }
- final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale);
- final String secondWord;
- if (wasAutoCapitalized) {
- if (isValidSuggestionWord(word) && !isValidSuggestionWord(lowerCasedWord)) {
- // If the word was auto-capitalized and exists only as a capitalized word in the
- // dictionary, then we must not downcase it before registering it. For example,
- // the name of the contacts in start-of-sentence position would come here with the
- // wasAutoCapitalized flag: if we downcase it, we'd register a lower-case version
- // of that contact's name which would end up popping in suggestions.
- secondWord = word;
- } else {
- // If however the word is not in the dictionary, or exists as a lower-case word
- // only, then we consider that was a lower-case word that had been auto-capitalized.
- secondWord = lowerCasedWord;
- }
- } else {
- // HACK: We'd like to avoid adding the capitalized form of common words to the User
- // History dictionary in order to avoid suggesting them until the dictionary
- // consolidation is done.
- // TODO: Remove this hack when ready.
- final int lowerCaseFreqInMainDict = dictionaryGroup.hasDict(Dictionary.TYPE_MAIN,
- null /* account */) ?
- dictionaryGroup.getDict(Dictionary.TYPE_MAIN).getFrequency(lowerCasedWord) :
- Dictionary.NOT_A_PROBABILITY;
- if (maxFreq < lowerCaseFreqInMainDict
- && lowerCaseFreqInMainDict >= CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT) {
- // Use lower cased word as the word can be a distracter of the popular word.
- secondWord = lowerCasedWord;
- } else {
- secondWord = word;
- }
- }
- // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
- // We don't add words with 0-frequency (assuming they would be profanity etc.).
- final boolean isValid = maxFreq > 0;
- UserHistoryDictionary.addToDictionary(userHistoryDictionary, ngramContext, secondWord,
- isValid, timeStampInSeconds);
- }
-
- private void removeWord(final String dictName, final String word) {
- final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName);
- if (dictionary != null) {
- dictionary.removeUnigramEntryDynamically(word);
- }
- }
-
- @Override
- public void unlearnFromUserHistory(final String word,
- @Nonnull final NgramContext ngramContext, final long timeStampInSeconds,
- final int eventType) {
- // TODO: Decide whether or not to remove the word on EVENT_BACKSPACE.
- if (eventType != Constants.EVENT_BACKSPACE) {
- removeWord(Dictionary.TYPE_USER_HISTORY, word);
- }
-
- // Update the spelling cache after unlearning. Words that are removed from user history
- // and appear in no other language model are not considered valid.
- putWordIntoValidSpellingWordCache("unlearnFromUserHistory", word.toLowerCase());
- }
-
- // TODO: Revise the way to fusion suggestion results.
- @Override
- @Nonnull public SuggestionResults getSuggestionResults(ComposedData composedData,
- NgramContext ngramContext, @Nonnull final Keyboard keyboard,
- SettingsValuesForSuggestion settingsValuesForSuggestion, int sessionId,
- int inputStyle) {
- long proximityInfoHandle = keyboard.getProximityInfo().getNativeProximityInfo();
- final SuggestionResults suggestionResults = new SuggestionResults(
- SuggestedWords.MAX_SUGGESTIONS, ngramContext.isBeginningOfSentenceContext(),
- false /* firstSuggestionExceedsConfidenceThreshold */);
- final float[] weightOfLangModelVsSpatialModel =
- new float[] { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL };
- for (final String dictType : ALL_DICTIONARY_TYPES) {
- final Dictionary dictionary = mDictionaryGroup.getDict(dictType);
- if (null == dictionary) continue;
- final float weightForLocale = composedData.mIsBatchMode
- ? mDictionaryGroup.mWeightForGesturingInLocale
- : mDictionaryGroup.mWeightForTypingInLocale;
- final ArrayList<SuggestedWordInfo> dictionarySuggestions =
- dictionary.getSuggestions(composedData, ngramContext,
- proximityInfoHandle, settingsValuesForSuggestion, sessionId,
- weightForLocale, weightOfLangModelVsSpatialModel);
- if (null == dictionarySuggestions) continue;
- suggestionResults.addAll(dictionarySuggestions);
- if (null != suggestionResults.mRawSuggestions) {
- suggestionResults.mRawSuggestions.addAll(dictionarySuggestions);
- }
- }
- return suggestionResults;
- }
-
- public boolean isValidSpellingWord(final String word) {
- if (mValidSpellingWordReadCache != null) {
- final Boolean cachedValue = mValidSpellingWordReadCache.get(word);
- if (cachedValue != null) {
- return cachedValue;
- }
- }
-
- return isValidWord(word, ALL_DICTIONARY_TYPES);
- }
-
- public boolean isValidSuggestionWord(final String word) {
- return isValidWord(word, ALL_DICTIONARY_TYPES);
- }
-
- private boolean isValidWord(final String word, final String[] dictionariesToCheck) {
- if (TextUtils.isEmpty(word)) {
- return false;
- }
- if (mDictionaryGroup.mLocale == null) {
- return false;
- }
- for (final String dictType : dictionariesToCheck) {
- final Dictionary dictionary = mDictionaryGroup.getDict(dictType);
- // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and
- // would be immutable once it's finished initializing, but concretely a null test is
- // probably good enough for the time being.
- if (null == dictionary) continue;
- if (dictionary.isValidWord(word)) {
- return true;
- }
- }
- return false;
- }
-
- private int getFrequency(final String word) {
- if (TextUtils.isEmpty(word)) {
- return Dictionary.NOT_A_PROBABILITY;
- }
- int maxFreq = Dictionary.NOT_A_PROBABILITY;
- for (final String dictType : ALL_DICTIONARY_TYPES) {
- final Dictionary dictionary = mDictionaryGroup.getDict(dictType);
- if (dictionary == null) continue;
- final int tempFreq = dictionary.getFrequency(word);
- if (tempFreq >= maxFreq) {
- maxFreq = tempFreq;
- }
- }
- return maxFreq;
- }
-
- private boolean clearSubDictionary(final String dictName) {
- final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName);
- if (dictionary == null) {
- return false;
- }
- dictionary.clear();
- return true;
- }
-
- @Override
- public boolean clearUserHistoryDictionary(final Context context) {
- return clearSubDictionary(Dictionary.TYPE_USER_HISTORY);
- }
-
- @Override
- public void dumpDictionaryForDebug(final String dictName) {
- final ExpandableBinaryDictionary dictToDump = mDictionaryGroup.getSubDict(dictName);
- if (dictToDump == null) {
- Log.e(TAG, "Cannot dump " + dictName + ". "
- + "The dictionary is not being used for suggestion or cannot be dumped.");
- return;
- }
- dictToDump.dumpAllWordsForDebug();
- }
-
- @Override
- @Nonnull public List<DictionaryStats> getDictionaryStats(final Context context) {
- final ArrayList<DictionaryStats> statsOfEnabledSubDicts = new ArrayList<>();
- for (final String dictType : DYNAMIC_DICTIONARY_TYPES) {
- final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictType);
- if (dictionary == null) continue;
- statsOfEnabledSubDicts.add(dictionary.getDictionaryStats());
- }
- return statsOfEnabledSubDicts;
- }
-
- @Override
- public String dump(final Context context) {
- return "";
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java
deleted file mode 100644
index cbaf6ea4e..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-import android.content.Context;
-import android.util.Log;
-
-/**
- * Cache for dictionary facilitators of multiple locales.
- * This class automatically creates and releases up to 3 facilitator instances using LRU policy.
- */
-public class DictionaryFacilitatorLruCache {
- private static final String TAG = "DictionaryFacilitatorLruCache";
- private static final int WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS = 1000;
- private static final int MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT = 5;
-
- private final Context mContext;
- private final String mDictionaryNamePrefix;
- private final Object mLock = new Object();
- private final DictionaryFacilitator mDictionaryFacilitator;
- private boolean mUseContactsDictionary;
- private Locale mLocale;
-
- public DictionaryFacilitatorLruCache(final Context context, final String dictionaryNamePrefix) {
- mContext = context;
- mDictionaryNamePrefix = dictionaryNamePrefix;
- mDictionaryFacilitator = DictionaryFacilitatorProvider.getDictionaryFacilitator(
- true /* isNeededForSpellChecking */);
- }
-
- private static void waitForLoadingMainDictionary(
- final DictionaryFacilitator dictionaryFacilitator) {
- for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) {
- try {
- dictionaryFacilitator.waitForLoadingMainDictionaries(
- WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
- return;
- } catch (final InterruptedException e) {
- Log.i(TAG, "Interrupted during waiting for loading main dictionary.", e);
- if (i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT - 1) {
- Log.i(TAG, "Retry", e);
- } else {
- Log.w(TAG, "Give up retrying. Retried "
- + MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT + " times.", e);
- }
- }
- }
- }
-
- private void resetDictionariesForLocaleLocked() {
- // Nothing to do if the locale is null. This would be the case before any get() calls.
- if (mLocale != null) {
- // Note: Given that personalized dictionaries are not used here; we can pass null account.
- mDictionaryFacilitator.resetDictionaries(mContext, mLocale,
- mUseContactsDictionary, false /* usePersonalizedDicts */,
- false /* forceReloadMainDictionary */, null /* account */,
- mDictionaryNamePrefix, null /* listener */);
- }
- }
-
- public void setUseContactsDictionary(final boolean useContactsDictionary) {
- synchronized (mLock) {
- if (mUseContactsDictionary == useContactsDictionary) {
- // The value has not been changed.
- return;
- }
- mUseContactsDictionary = useContactsDictionary;
- resetDictionariesForLocaleLocked();
- waitForLoadingMainDictionary(mDictionaryFacilitator);
- }
- }
-
- public DictionaryFacilitator get(final Locale locale) {
- synchronized (mLock) {
- if (!mDictionaryFacilitator.isForLocale(locale)) {
- mLocale = locale;
- resetDictionariesForLocaleLocked();
- }
- waitForLoadingMainDictionary(mDictionaryFacilitator);
- return mDictionaryFacilitator;
- }
- }
-
- public void closeDictionaries() {
- synchronized (mLock) {
- mDictionaryFacilitator.closeDictionaries();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorProvider.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorProvider.java
deleted file mode 100644
index a48b41fa7..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorProvider.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-/**
- * Factory for instantiating DictionaryFacilitator objects.
- */
-public class DictionaryFacilitatorProvider {
- public static DictionaryFacilitator getDictionaryFacilitator(boolean isNeededForSpellChecking) {
- return new DictionaryFacilitatorImpl();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
deleted file mode 100644
index 5dd02bd1c..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.util.Log;
-
-import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.Locale;
-
-/**
- * Factory for dictionary instances.
- */
-public final class DictionaryFactory {
- private static final String TAG = DictionaryFactory.class.getSimpleName();
-
- /**
- * Initializes a main dictionary collection from a dictionary pack, with explicit flags.
- *
- * This searches for a content provider providing a dictionary pack for the specified
- * locale. If none is found, it falls back to the built-in dictionary - if any.
- * @param context application context for reading resources
- * @param locale the locale for which to create the dictionary
- * @return an initialized instance of DictionaryCollection
- */
- public static DictionaryCollection createMainDictionaryFromManager(final Context context,
- final Locale locale) {
- if (null == locale) {
- Log.e(TAG, "No locale defined for dictionary");
- return new DictionaryCollection(Dictionary.TYPE_MAIN, locale,
- createReadOnlyBinaryDictionary(context, locale));
- }
-
- final LinkedList<Dictionary> dictList = new LinkedList<>();
- final ArrayList<AssetFileAddress> assetFileList =
- BinaryDictionaryGetter.getDictionaryFiles(locale, context, true);
- if (null != assetFileList) {
- for (final AssetFileAddress f : assetFileList) {
- final ReadOnlyBinaryDictionary readOnlyBinaryDictionary =
- new ReadOnlyBinaryDictionary(f.mFilename, f.mOffset, f.mLength,
- false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN);
- if (readOnlyBinaryDictionary.isValidDictionary()) {
- dictList.add(readOnlyBinaryDictionary);
- } else {
- readOnlyBinaryDictionary.close();
- // Prevent this dictionary to do any further harm.
- killDictionary(context, f);
- }
- }
- }
-
- // If the list is empty, that means we should not use any dictionary (for example, the user
- // explicitly disabled the main dictionary), so the following is okay. dictList is never
- // null, but if for some reason it is, DictionaryCollection handles it gracefully.
- return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList);
- }
-
- /**
- * Kills a dictionary so that it is never used again, if possible.
- * @param context The context to contact the dictionary provider, if possible.
- * @param f A file address to the dictionary to kill.
- */
- public static void killDictionary(final Context context, final AssetFileAddress f) {
- if (f.pointsToPhysicalFile()) {
- f.deleteUnderlyingFile();
- // Warn the dictionary provider if the dictionary came from there.
- final ContentProviderClient providerClient;
- try {
- providerClient = context.getContentResolver().acquireContentProviderClient(
- BinaryDictionaryFileDumper.getProviderUriBuilder("").build());
- } catch (final SecurityException e) {
- Log.e(TAG, "No permission to communicate with the dictionary provider", e);
- return;
- }
- if (null == providerClient) {
- Log.e(TAG, "Can't establish communication with the dictionary provider");
- return;
- }
- final String wordlistId =
- DictionaryInfoUtils.getWordListIdFromFileName(new File(f.mFilename).getName());
- // TODO: this is a reasonable last resort, but it is suboptimal.
- // The following will remove the entry for this dictionary with the dictionary
- // provider. When the metadata is downloaded again, we will try downloading it
- // again.
- // However, in the practice that will mean the user will find themselves without
- // the new dictionary. That's fine for languages where it's included in the APK,
- // but for other languages it will leave the user without a dictionary at all until
- // the next update, which may be a few days away.
- // Ideally, we would trigger a new download right away, and use increasing retry
- // delays for this particular id/version combination.
- // Then again, this is expected to only ever happen in case of human mistake. If
- // the wrong file is on the server, the following is still doing the right thing.
- // If it's a file left over from the last version however, it's not great.
- BinaryDictionaryFileDumper.reportBrokenFileToDictionaryProvider(
- providerClient,
- context.getString(R.string.dictionary_pack_client_id),
- wordlistId);
- }
- }
-
- /**
- * Initializes a read-only binary dictionary from a raw resource file
- * @param context application context for reading resources
- * @param locale the locale to use for the resource
- * @return an initialized instance of ReadOnlyBinaryDictionary
- */
- private static ReadOnlyBinaryDictionary createReadOnlyBinaryDictionary(final Context context,
- final Locale locale) {
- AssetFileDescriptor afd = null;
- try {
- final int resId = DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale(
- context.getResources(), locale);
- if (0 == resId) return null;
- afd = context.getResources().openRawResourceFd(resId);
- if (afd == null) {
- Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
- return null;
- }
- final String sourceDir = context.getApplicationInfo().sourceDir;
- final File packagePath = new File(sourceDir);
- // TODO: Come up with a way to handle a directory.
- if (!packagePath.isFile()) {
- Log.e(TAG, "sourceDir is not a file: " + sourceDir);
- return null;
- }
- return new ReadOnlyBinaryDictionary(sourceDir, afd.getStartOffset(), afd.getLength(),
- false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN);
- } catch (android.content.res.Resources.NotFoundException e) {
- Log.e(TAG, "Could not find the resource");
- return null;
- } finally {
- if (null != afd) {
- try {
- afd.close();
- } catch (java.io.IOException e) {
- /* IOException on close ? What am I supposed to do ? */
- }
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
deleted file mode 100644
index 2dcfdb0b7..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
-import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.net.Uri;
-import android.util.Log;
-
-/**
- * Receives broadcasts pertaining to dictionary management and takes the appropriate action.
- *
- * This object receives three types of broadcasts.
- * - Package installed/added. When a dictionary provider application is added or removed, we
- * need to query the dictionaries.
- * - New dictionary broadcast. The dictionary provider broadcasts new dictionary availability. When
- * this happens, we need to re-query the dictionaries.
- * - Unknown client. If the dictionary provider is in urgent need of data about some client that
- * it does not know, it sends this broadcast. When we receive this, we need to tell the dictionary
- * provider about ourselves. This happens when the settings for the dictionary pack are accessed,
- * but Latin IME never got a chance to register itself.
- */
-public final class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = DictionaryPackInstallBroadcastReceiver.class.getSimpleName();
-
- final LatinIME mService;
-
- public DictionaryPackInstallBroadcastReceiver() {
- // This empty constructor is necessary for the system to instantiate this receiver.
- // This happens when the dictionary pack says it can't find a record for our client,
- // which happens when the dictionary pack settings are called before the keyboard
- // was ever started once.
- Log.i(TAG, "Latin IME dictionary broadcast receiver instantiated from the framework.");
- mService = null;
- }
-
- public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
- mService = service;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- final PackageManager manager = context.getPackageManager();
-
- // We need to reread the dictionary if a new dictionary package is installed.
- if (action.equals(Intent.ACTION_PACKAGE_ADDED)) {
- if (null == mService) {
- Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
- + "should never happen");
- return;
- }
- final Uri packageUri = intent.getData();
- if (null == packageUri) return; // No package name : we can't do anything
- final String packageName = packageUri.getSchemeSpecificPart();
- if (null == packageName) return;
- // TODO: do this in a more appropriate place
- TargetPackageInfoGetterTask.removeCachedPackageInfo(packageName);
- final PackageInfo packageInfo;
- try {
- packageInfo = manager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS);
- } catch (android.content.pm.PackageManager.NameNotFoundException e) {
- return; // No package info : we can't do anything
- }
- final ProviderInfo[] providers = packageInfo.providers;
- if (null == providers) return; // No providers : it is not a dictionary.
-
- // Search for some dictionary pack in the just-installed package. If found, reread.
- for (ProviderInfo info : providers) {
- if (DictionaryPackConstants.AUTHORITY.equals(info.authority)) {
- mService.resetSuggestMainDict();
- return;
- }
- }
- // If we come here none of the authorities matched the one we searched for.
- // We can exit safely.
- return;
- } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
- && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- if (null == mService) {
- Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
- + "should never happen");
- return;
- }
- // When the dictionary package is removed, we need to reread dictionary (to use the
- // next-priority one, or stop using a dictionary at all if this was the only one,
- // since this is the user request).
- // If we are replacing the package, we will receive ADDED right away so no need to
- // remove the dictionary at the moment, since we will do it when we receive the
- // ADDED broadcast.
-
- // TODO: Only reload dictionary on REMOVED when the removed package is the one we
- // read dictionary from?
- mService.resetSuggestMainDict();
- } else if (action.equals(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)) {
- if (null == mService) {
- Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
- + "should never happen");
- return;
- }
- mService.resetSuggestMainDict();
- } else if (action.equals(DictionaryPackConstants.UNKNOWN_DICTIONARY_PROVIDER_CLIENT)) {
- if (null != mService) {
- // Careful! This is returning if the service is NOT null. This is because we
- // should come here instantiated by the framework in reaction to a broadcast of
- // the above action, so we should gave gone through the no-args constructor.
- Log.e(TAG, "Called with intent " + action + " but we have a reference to the "
- + "service: this should never happen");
- return;
- }
- // The dictionary provider does not know about some client. We check that it's really
- // us that it needs to know about, and if it's the case, we register with the provider.
- final String wantedClientId =
- intent.getStringExtra(DictionaryPackConstants.DICTIONARY_PROVIDER_CLIENT_EXTRA);
- final String myClientId = context.getString(R.string.dictionary_pack_client_id);
- if (!wantedClientId.equals(myClientId)) return; // Not for us
- BinaryDictionaryFileDumper.initializeClientRecordHelper(context, myClientId);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryStats.java b/java/src/com/android/inputmethod/latin/DictionaryStats.java
deleted file mode 100644
index b65d25bb1..000000000
--- a/java/src/com/android/inputmethod/latin/DictionaryStats.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import java.io.File;
-import java.math.BigDecimal;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class DictionaryStats {
- public static final int NOT_AN_ENTRY_COUNT = -1;
-
- public final Locale mLocale;
- public final String mDictType;
- public final String mDictFileName;
- public final long mDictFileSize;
- public final int mContentVersion;
- public final int mWordCount;
-
- public DictionaryStats(
- @Nonnull final Locale locale,
- @Nonnull final String dictType,
- @Nullable final String dictFileName,
- @Nullable final File dictFile,
- final int contentVersion) {
- mLocale = locale;
- mDictType = dictType;
- mDictFileSize = (dictFile == null || !dictFile.exists()) ? 0 : dictFile.length();
- mDictFileName = dictFileName;
- mContentVersion = contentVersion;
- mWordCount = -1;
- }
-
- public DictionaryStats(
- @Nonnull final Locale locale,
- @Nonnull final String dictType,
- final int wordCount) {
- mLocale = locale;
- mDictType = dictType;
- mDictFileSize = wordCount;
- mDictFileName = null;
- mContentVersion = 0;
- mWordCount = wordCount;
- }
-
- public String getFileSizeString() {
- BigDecimal bytes = new BigDecimal(mDictFileSize);
- BigDecimal kb = bytes.divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP);
- if (kb.longValue() == 0) {
- return bytes.toString() + " bytes";
- }
- BigDecimal mb = kb.divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP);
- if (mb.longValue() == 0) {
- return kb.toString() + " kb";
- }
- return mb.toString() + " Mb";
- }
-
- @Override
- public String toString() {
- final StringBuilder builder = new StringBuilder(mDictType);
- if (mDictType.equals(Dictionary.TYPE_MAIN)) {
- builder.append(" (");
- builder.append(mContentVersion);
- builder.append(")");
- }
- builder.append(": ");
- if (mWordCount > -1) {
- builder.append(mWordCount);
- builder.append(" words");
- } else {
- builder.append(mDictFileName);
- builder.append(" / ");
- builder.append(getFileSizeString());
- }
- return builder.toString();
- }
-
- public static String toString(final Iterable<DictionaryStats> stats) {
- final StringBuilder builder = new StringBuilder("LM Stats");
- for (DictionaryStats stat : stats) {
- builder.append("\n ");
- builder.append(stat.toString());
- }
- return builder.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java
deleted file mode 100644
index 8924e0a3d..000000000
--- a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.res.Resources;
-import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.latin.settings.Settings;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-
-/**
- * A class for detecting Emoji-Alt physical key.
- */
-final class EmojiAltPhysicalKeyDetector {
- private static final String TAG = "EmojiAltPhysicalKeyDetector";
- private static final boolean DEBUG = false;
-
- private List<EmojiHotKeys> mHotKeysList;
-
- private static class HotKeySet extends HashSet<Pair<Integer, Integer>> { };
-
- private abstract class EmojiHotKeys {
- private final String mName;
- private final HotKeySet mKeySet;
-
- boolean mCanFire;
- int mMetaState;
-
- public EmojiHotKeys(final String name, HotKeySet keySet) {
- mName = name;
- mKeySet = keySet;
- mCanFire = false;
- }
-
- public void onKeyDown(@Nonnull final KeyEvent keyEvent) {
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - considering " + keyEvent);
- }
-
- final Pair<Integer, Integer> key =
- Pair.create(keyEvent.getKeyCode(), keyEvent.getMetaState());
- if (mKeySet.contains(key)) {
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - enabling action");
- }
- mCanFire = true;
- mMetaState = keyEvent.getMetaState();
- } else if (mCanFire) {
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - disabling action");
- }
- mCanFire = false;
- }
- }
-
- public void onKeyUp(@Nonnull final KeyEvent keyEvent) {
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - considering " + keyEvent);
- }
-
- final int keyCode = keyEvent.getKeyCode();
- int metaState = keyEvent.getMetaState();
- if (KeyEvent.isModifierKey(keyCode)) {
- // Try restoring meta stat in case the released key was a modifier.
- // I am sure one can come up with scenarios to break this, but it
- // seems to work well in practice.
- metaState |= mMetaState;
- }
-
- final Pair<Integer, Integer> key = Pair.create(keyCode, metaState);
- if (mKeySet.contains(key)) {
- if (mCanFire) {
- if (!keyEvent.isCanceled()) {
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - firing action");
- }
- action();
- } else {
- // This key up event was a part of key combinations and
- // should be ignored.
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - canceled, ignoring action");
- }
- }
- mCanFire = false;
- }
- }
-
- if (mCanFire) {
- if (DEBUG) {
- Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - disabling action");
- }
- mCanFire = false;
- }
- }
-
- protected abstract void action();
- }
-
- public EmojiAltPhysicalKeyDetector(@Nonnull final Resources resources) {
- mHotKeysList = new ArrayList<EmojiHotKeys>();
-
- final HotKeySet emojiSwitchSet = parseHotKeys(
- resources, R.array.keyboard_switcher_emoji);
- final EmojiHotKeys emojiHotKeys = new EmojiHotKeys("emoji", emojiSwitchSet) {
- @Override
- protected void action() {
- final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
- switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.EMOJI);
- }
- };
- mHotKeysList.add(emojiHotKeys);
-
- final HotKeySet symbolsSwitchSet = parseHotKeys(
- resources, R.array.keyboard_switcher_symbols_shifted);
- final EmojiHotKeys symbolsHotKeys = new EmojiHotKeys("symbols", symbolsSwitchSet) {
- @Override
- protected void action() {
- final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
- switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.SYMBOLS_SHIFTED);
- }
- };
- mHotKeysList.add(symbolsHotKeys);
- }
-
- public void onKeyDown(@Nonnull final KeyEvent keyEvent) {
- if (DEBUG) {
- Log.d(TAG, "onKeyDown(): " + keyEvent);
- }
-
- if (shouldProcessEvent(keyEvent)) {
- for (EmojiHotKeys hotKeys : mHotKeysList) {
- hotKeys.onKeyDown(keyEvent);
- }
- }
- }
-
- public void onKeyUp(@Nonnull final KeyEvent keyEvent) {
- if (DEBUG) {
- Log.d(TAG, "onKeyUp(): " + keyEvent);
- }
-
- if (shouldProcessEvent(keyEvent)) {
- for (EmojiHotKeys hotKeys : mHotKeysList) {
- hotKeys.onKeyUp(keyEvent);
- }
- }
- }
-
- private static boolean shouldProcessEvent(@Nonnull final KeyEvent keyEvent) {
- if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) {
- // The feature is disabled.
- if (DEBUG) {
- Log.d(TAG, "shouldProcessEvent(): Disabled");
- }
- return false;
- }
-
- return true;
- }
-
- private static HotKeySet parseHotKeys(
- @Nonnull final Resources resources, final int resourceId) {
- final HotKeySet keySet = new HotKeySet();
- final String name = resources.getResourceEntryName(resourceId);
- final String[] values = resources.getStringArray(resourceId);
- for (int i = 0; values != null && i < values.length; i++) {
- String[] valuePair = values[i].split(",");
- if (valuePair.length != 2) {
- Log.w(TAG, "Expected 2 integers in " + name + "[" + i + "] : " + values[i]);
- }
- try {
- final Integer keyCode = Integer.parseInt(valuePair[0]);
- final Integer metaState = Integer.parseInt(valuePair[1]);
- final Pair<Integer, Integer> key = Pair.create(
- keyCode, KeyEvent.normalizeMetaState(metaState));
- keySet.add(key);
- } catch (NumberFormatException e) {
- Log.w(TAG, "Failed to parse " + name + "[" + i + "] : " + values[i], e);
- }
- }
- return keySet;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
deleted file mode 100644
index 907095746..000000000
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.common.FileUtils;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.WordProperty;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.utils.AsyncResultHolder;
-import com.android.inputmethod.latin.utils.CombinedFormatUtils;
-import com.android.inputmethod.latin.utils.ExecutorUtils;
-import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Abstract base class for an expandable dictionary that can be created and updated dynamically
- * during runtime. When updated it automatically generates a new binary dictionary to handle future
- * queries in native code. This binary dictionary is written to internal storage.
- *
- * A class that extends this abstract class must have a static factory method named
- * getDictionary(Context context, Locale locale, File dictFile, String dictNamePrefix)
- */
-abstract public class ExpandableBinaryDictionary extends Dictionary {
- private static final boolean DEBUG = false;
-
- /** Used for Log actions from this class */
- private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName();
-
- /** Whether to print debug output to log */
- private static final boolean DBG_STRESS_TEST = false;
-
- private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
-
- /**
- * The maximum length of a word in this dictionary.
- */
- protected static final int MAX_WORD_LENGTH =
- DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH;
-
- private static final int DICTIONARY_FORMAT_VERSION = FormatSpec.VERSION4;
-
- private static final WordProperty[] DEFAULT_WORD_PROPERTIES_FOR_SYNC =
- new WordProperty[0] /* default */;
-
- /** The application context. */
- protected final Context mContext;
-
- /**
- * The binary dictionary generated dynamically from the fusion dictionary. This is used to
- * answer unigram and bigram queries.
- */
- private BinaryDictionary mBinaryDictionary;
-
- /**
- * The name of this dictionary, used as a part of the filename for storing the binary
- * dictionary.
- */
- private final String mDictName;
-
- /** Dictionary file */
- private final File mDictFile;
-
- /** Indicates whether a task for reloading the dictionary has been scheduled. */
- private final AtomicBoolean mIsReloading;
-
- /** Indicates whether the current dictionary needs to be recreated. */
- private boolean mNeedsToRecreate;
-
- private final ReentrantReadWriteLock mLock;
-
- private Map<String, String> mAdditionalAttributeMap = null;
-
- /* A extension for a binary dictionary file. */
- protected static final String DICT_FILE_EXTENSION = ".dict";
-
- /**
- * Abstract method for loading initial contents of a given dictionary.
- */
- protected abstract void loadInitialContentsLocked();
-
- static boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) {
- return formatVersion == FormatSpec.VERSION4;
- }
-
- private static boolean needsToMigrateDictionary(final int formatVersion) {
- // When we bump up the dictionary format version, the old version should be added to here
- // for supporting migration. Note that native code has to support reading such formats.
- return formatVersion == FormatSpec.VERSION402;
- }
-
- public boolean isValidDictionaryLocked() {
- return mBinaryDictionary.isValidDictionary();
- }
-
- /**
- * Creates a new expandable binary dictionary.
- *
- * @param context The application context of the parent.
- * @param dictName The name of the dictionary. Multiple instances with the same
- * name is supported.
- * @param locale the dictionary locale.
- * @param dictType the dictionary type, as a human-readable string
- * @param dictFile dictionary file path. if null, use default dictionary path based on
- * dictionary type.
- */
- public ExpandableBinaryDictionary(final Context context, final String dictName,
- final Locale locale, final String dictType, final File dictFile) {
- super(dictType, locale);
- mDictName = dictName;
- mContext = context;
- mDictFile = getDictFile(context, dictName, dictFile);
- mBinaryDictionary = null;
- mIsReloading = new AtomicBoolean();
- mNeedsToRecreate = false;
- mLock = new ReentrantReadWriteLock();
- }
-
- public static File getDictFile(final Context context, final String dictName,
- final File dictFile) {
- return (dictFile != null) ? dictFile
- : new File(context.getFilesDir(), dictName + DICT_FILE_EXTENSION);
- }
-
- public static String getDictName(final String name, final Locale locale,
- final File dictFile) {
- return dictFile != null ? dictFile.getName() : name + "." + locale.toString();
- }
-
- private void asyncExecuteTaskWithWriteLock(final Runnable task) {
- asyncExecuteTaskWithLock(mLock.writeLock(), task);
- }
-
- private static void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) {
- ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() {
- @Override
- public void run() {
- lock.lock();
- try {
- task.run();
- } finally {
- lock.unlock();
- }
- }
- });
- }
-
- @Nullable
- BinaryDictionary getBinaryDictionary() {
- return mBinaryDictionary;
- }
-
- void closeBinaryDictionary() {
- if (mBinaryDictionary != null) {
- mBinaryDictionary.close();
- mBinaryDictionary = null;
- }
- }
-
- /**
- * Closes and cleans up the binary dictionary.
- */
- @Override
- public void close() {
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- closeBinaryDictionary();
- }
- });
- }
-
- protected Map<String, String> getHeaderAttributeMap() {
- HashMap<String, String> attributeMap = new HashMap<>();
- if (mAdditionalAttributeMap != null) {
- attributeMap.putAll(mAdditionalAttributeMap);
- }
- attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, mDictName);
- attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
- attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
- String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
- return attributeMap;
- }
-
- private void removeBinaryDictionary() {
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- removeBinaryDictionaryLocked();
- }
- });
- }
-
- void removeBinaryDictionaryLocked() {
- closeBinaryDictionary();
- if (mDictFile.exists() && !FileUtils.deleteRecursively(mDictFile)) {
- Log.e(TAG, "Can't remove a file: " + mDictFile.getName());
- }
- }
-
- private void openBinaryDictionaryLocked() {
- mBinaryDictionary = new BinaryDictionary(
- mDictFile.getAbsolutePath(), 0 /* offset */, mDictFile.length(),
- true /* useFullEditDistance */, mLocale, mDictType, true /* isUpdatable */);
- }
-
- void createOnMemoryBinaryDictionaryLocked() {
- mBinaryDictionary = new BinaryDictionary(
- mDictFile.getAbsolutePath(), true /* useFullEditDistance */, mLocale, mDictType,
- DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
- }
-
- public void clear() {
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- removeBinaryDictionaryLocked();
- createOnMemoryBinaryDictionaryLocked();
- }
- });
- }
-
- /**
- * Check whether GC is needed and run GC if required.
- */
- public void runGCIfRequired(final boolean mindsBlockByGC) {
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- if (getBinaryDictionary() == null) {
- return;
- }
- runGCIfRequiredLocked(mindsBlockByGC);
- }
- });
- }
-
- protected void runGCIfRequiredLocked(final boolean mindsBlockByGC) {
- if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
- mBinaryDictionary.flushWithGC();
- }
- }
-
- private void updateDictionaryWithWriteLock(@Nonnull final Runnable updateTask) {
- reloadDictionaryIfRequired();
- final Runnable task = new Runnable() {
- @Override
- public void run() {
- if (getBinaryDictionary() == null) {
- return;
- }
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- updateTask.run();
- }
- };
- asyncExecuteTaskWithWriteLock(task);
- }
-
- /**
- * Adds unigram information of a word to the dictionary. May overwrite an existing entry.
- */
- public void addUnigramEntry(final String word, final int frequency,
- final boolean isNotAWord, final boolean isPossiblyOffensive, final int timestamp) {
- updateDictionaryWithWriteLock(new Runnable() {
- @Override
- public void run() {
- addUnigramLocked(word, frequency, isNotAWord, isPossiblyOffensive, timestamp);
- }
- });
- }
-
- protected void addUnigramLocked(final String word, final int frequency,
- final boolean isNotAWord, final boolean isPossiblyOffensive, final int timestamp) {
- if (!mBinaryDictionary.addUnigramEntry(word, frequency,
- false /* isBeginningOfSentence */, isNotAWord, isPossiblyOffensive, timestamp)) {
- Log.e(TAG, "Cannot add unigram entry. word: " + word);
- }
- }
-
- /**
- * Dynamically remove the unigram entry from the dictionary.
- */
- public void removeUnigramEntryDynamically(final String word) {
- reloadDictionaryIfRequired();
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary == null) {
- return;
- }
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- if (!binaryDictionary.removeUnigramEntry(word)) {
- if (DEBUG) {
- Log.i(TAG, "Cannot remove unigram entry: " + word);
- }
- }
- }
- });
- }
-
- /**
- * Adds n-gram information of a word to the dictionary. May overwrite an existing entry.
- */
- public void addNgramEntry(@Nonnull final NgramContext ngramContext, final String word,
- final int frequency, final int timestamp) {
- reloadDictionaryIfRequired();
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- if (getBinaryDictionary() == null) {
- return;
- }
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addNgramEntryLocked(ngramContext, word, frequency, timestamp);
- }
- });
- }
-
- protected void addNgramEntryLocked(@Nonnull final NgramContext ngramContext, final String word,
- final int frequency, final int timestamp) {
- if (!mBinaryDictionary.addNgramEntry(ngramContext, word, frequency, timestamp)) {
- if (DEBUG) {
- Log.i(TAG, "Cannot add n-gram entry.");
- Log.i(TAG, " NgramContext: " + ngramContext + ", word: " + word);
- }
- }
- }
-
- /**
- * Update dictionary for the word with the ngramContext.
- */
- public void updateEntriesForWord(@Nonnull final NgramContext ngramContext,
- final String word, final boolean isValidWord, final int count, final int timestamp) {
- updateDictionaryWithWriteLock(new Runnable() {
- @Override
- public void run() {
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary == null) {
- return;
- }
- if (!binaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word,
- isValidWord, count, timestamp)) {
- if (DEBUG) {
- Log.e(TAG, "Cannot update counter. word: " + word
- + " context: " + ngramContext.toString());
- }
- }
- }
- });
- }
-
- /**
- * Used by Sketch.
- * {@see https://cs.corp.google.com/#android/vendor/unbundled_google/packages/LatinIMEGoogle/tools/sketch/ime-simulator/src/com/android/inputmethod/sketch/imesimulator/ImeSimulator.java&q=updateEntriesForInputEventsCallback&l=286}
- */
- @UsedForTesting
- public interface UpdateEntriesForInputEventsCallback {
- public void onFinished();
- }
-
- /**
- * Dynamically update entries according to input events.
- *
- * Used by Sketch.
- * {@see https://cs.corp.google.com/#android/vendor/unbundled_google/packages/LatinIMEGoogle/tools/sketch/ime-simulator/src/com/android/inputmethod/sketch/imesimulator/ImeSimulator.java&q=updateEntriesForInputEventsCallback&l=286}
- */
- @UsedForTesting
- public void updateEntriesForInputEvents(
- @Nonnull final ArrayList<WordInputEventForPersonalization> inputEvents,
- final UpdateEntriesForInputEventsCallback callback) {
- reloadDictionaryIfRequired();
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- try {
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary == null) {
- return;
- }
- binaryDictionary.updateEntriesForInputEvents(
- inputEvents.toArray(
- new WordInputEventForPersonalization[inputEvents.size()]));
- } finally {
- if (callback != null) {
- callback.onFinished();
- }
- }
- }
- });
- }
-
- @Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
- final NgramContext ngramContext, final long proximityInfoHandle,
- final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId,
- final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) {
- reloadDictionaryIfRequired();
- boolean lockAcquired = false;
- try {
- lockAcquired = mLock.readLock().tryLock(
- TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
- if (lockAcquired) {
- if (mBinaryDictionary == null) {
- return null;
- }
- final ArrayList<SuggestedWordInfo> suggestions =
- mBinaryDictionary.getSuggestions(composedData, ngramContext,
- proximityInfoHandle, settingsValuesForSuggestion, sessionId,
- weightForLocale, inOutWeightOfLangModelVsSpatialModel);
- if (mBinaryDictionary.isCorrupted()) {
- Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. "
- + "Remove and regenerate it.");
- removeBinaryDictionary();
- }
- return suggestions;
- }
- } catch (final InterruptedException e) {
- Log.e(TAG, "Interrupted tryLock() in getSuggestionsWithSessionId().", e);
- } finally {
- if (lockAcquired) {
- mLock.readLock().unlock();
- }
- }
- return null;
- }
-
- @Override
- public boolean isInDictionary(final String word) {
- reloadDictionaryIfRequired();
- boolean lockAcquired = false;
- try {
- lockAcquired = mLock.readLock().tryLock(
- TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
- if (lockAcquired) {
- if (mBinaryDictionary == null) {
- return false;
- }
- return isInDictionaryLocked(word);
- }
- } catch (final InterruptedException e) {
- Log.e(TAG, "Interrupted tryLock() in isInDictionary().", e);
- } finally {
- if (lockAcquired) {
- mLock.readLock().unlock();
- }
- }
- return false;
- }
-
- protected boolean isInDictionaryLocked(final String word) {
- if (mBinaryDictionary == null) return false;
- return mBinaryDictionary.isInDictionary(word);
- }
-
- @Override
- public int getMaxFrequencyOfExactMatches(final String word) {
- reloadDictionaryIfRequired();
- boolean lockAcquired = false;
- try {
- lockAcquired = mLock.readLock().tryLock(
- TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
- if (lockAcquired) {
- if (mBinaryDictionary == null) {
- return NOT_A_PROBABILITY;
- }
- return mBinaryDictionary.getMaxFrequencyOfExactMatches(word);
- }
- } catch (final InterruptedException e) {
- Log.e(TAG, "Interrupted tryLock() in getMaxFrequencyOfExactMatches().", e);
- } finally {
- if (lockAcquired) {
- mLock.readLock().unlock();
- }
- }
- return NOT_A_PROBABILITY;
- }
-
-
- /**
- * Loads the current binary dictionary from internal storage. Assumes the dictionary file
- * exists.
- */
- void loadBinaryDictionaryLocked() {
- if (DBG_STRESS_TEST) {
- // Test if this class does not cause problems when it takes long time to load binary
- // dictionary.
- try {
- Log.w(TAG, "Start stress in loading: " + mDictName);
- Thread.sleep(15000);
- Log.w(TAG, "End stress in loading");
- } catch (InterruptedException e) {
- Log.w("Interrupted while loading: " + mDictName, e);
- }
- }
- final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
- openBinaryDictionaryLocked();
- if (oldBinaryDictionary != null) {
- oldBinaryDictionary.close();
- }
- if (mBinaryDictionary.isValidDictionary()
- && needsToMigrateDictionary(mBinaryDictionary.getFormatVersion())) {
- if (!mBinaryDictionary.migrateTo(DICTIONARY_FORMAT_VERSION)) {
- Log.e(TAG, "Dictionary migration failed: " + mDictName);
- removeBinaryDictionaryLocked();
- }
- }
- }
-
- /**
- * Create a new binary dictionary and load initial contents.
- */
- void createNewDictionaryLocked() {
- removeBinaryDictionaryLocked();
- createOnMemoryBinaryDictionaryLocked();
- loadInitialContentsLocked();
- // Run GC and flush to file when initial contents have been loaded.
- mBinaryDictionary.flushWithGCIfHasUpdated();
- }
-
- /**
- * Marks that the dictionary needs to be recreated.
- *
- */
- protected void setNeedsToRecreate() {
- mNeedsToRecreate = true;
- }
-
- void clearNeedsToRecreate() {
- mNeedsToRecreate = false;
- }
-
- boolean isNeededToRecreate() {
- return mNeedsToRecreate;
- }
-
- /**
- * Load the current binary dictionary from internal storage. If the dictionary file doesn't
- * exists or needs to be regenerated, the new dictionary file will be asynchronously generated.
- * However, the dictionary itself is accessible even before the new dictionary file is actually
- * generated. It may return a null result for getSuggestions() in that case by design.
- */
- public final void reloadDictionaryIfRequired() {
- if (!isReloadRequired()) return;
- asyncReloadDictionary();
- }
-
- /**
- * Returns whether a dictionary reload is required.
- */
- private boolean isReloadRequired() {
- return mBinaryDictionary == null || mNeedsToRecreate;
- }
-
- /**
- * Reloads the dictionary. Access is controlled on a per dictionary file basis.
- */
- private void asyncReloadDictionary() {
- final AtomicBoolean isReloading = mIsReloading;
- if (!isReloading.compareAndSet(false, true)) {
- return;
- }
- final File dictFile = mDictFile;
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- try {
- if (!dictFile.exists() || isNeededToRecreate()) {
- // If the dictionary file does not exist or contents have been updated,
- // generate a new one.
- createNewDictionaryLocked();
- } else if (getBinaryDictionary() == null) {
- // Otherwise, load the existing dictionary.
- loadBinaryDictionaryLocked();
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary != null && !(isValidDictionaryLocked()
- // TODO: remove the check below
- && matchesExpectedBinaryDictFormatVersionForThisType(
- binaryDictionary.getFormatVersion()))) {
- // Binary dictionary or its format version is not valid. Regenerate
- // the dictionary file. createNewDictionaryLocked will remove the
- // existing files if appropriate.
- createNewDictionaryLocked();
- }
- }
- clearNeedsToRecreate();
- } finally {
- isReloading.set(false);
- }
- }
- });
- }
-
- /**
- * Flush binary dictionary to dictionary file.
- */
- public void asyncFlushBinaryDictionary() {
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary == null) {
- return;
- }
- if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
- binaryDictionary.flushWithGC();
- } else {
- binaryDictionary.flush();
- }
- }
- });
- }
-
- public DictionaryStats getDictionaryStats() {
- reloadDictionaryIfRequired();
- final String dictName = mDictName;
- final File dictFile = mDictFile;
- final AsyncResultHolder<DictionaryStats> result =
- new AsyncResultHolder<>("DictionaryStats");
- asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() {
- @Override
- public void run() {
- result.set(new DictionaryStats(mLocale, dictName, dictName, dictFile, 0));
- }
- });
- return result.get(null /* defaultValue */, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
- }
-
- @UsedForTesting
- public void waitAllTasksForTests() {
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- asyncExecuteTaskWithWriteLock(new Runnable() {
- @Override
- public void run() {
- countDownLatch.countDown();
- }
- });
- try {
- countDownLatch.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e);
- }
- }
-
- @UsedForTesting
- public void clearAndFlushDictionaryWithAdditionalAttributes(
- final Map<String, String> attributeMap) {
- mAdditionalAttributeMap = attributeMap;
- clear();
- }
-
- public void dumpAllWordsForDebug() {
- reloadDictionaryIfRequired();
- final String tag = TAG;
- final String dictName = mDictName;
- asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() {
- @Override
- public void run() {
- Log.d(tag, "Dump dictionary: " + dictName + " for " + mLocale);
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary == null) {
- return;
- }
- try {
- final DictionaryHeader header = binaryDictionary.getHeader();
- Log.d(tag, "Format version: " + binaryDictionary.getFormatVersion());
- Log.d(tag, CombinedFormatUtils.formatAttributeMap(
- header.mDictionaryOptions.mAttributes));
- } catch (final UnsupportedFormatException e) {
- Log.d(tag, "Cannot fetch header information.", e);
- }
- int token = 0;
- do {
- final BinaryDictionary.GetNextWordPropertyResult result =
- binaryDictionary.getNextWordProperty(token);
- final WordProperty wordProperty = result.mWordProperty;
- if (wordProperty == null) {
- Log.d(tag, " dictionary is empty.");
- break;
- }
- Log.d(tag, wordProperty.toString());
- token = result.mNextToken;
- } while (token != 0);
- }
- });
- }
-
- /**
- * Returns dictionary content required for syncing.
- */
- public WordProperty[] getWordPropertiesForSyncing() {
- reloadDictionaryIfRequired();
- final AsyncResultHolder<WordProperty[]> result =
- new AsyncResultHolder<>("WordPropertiesForSync");
- asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() {
- @Override
- public void run() {
- final ArrayList<WordProperty> wordPropertyList = new ArrayList<>();
- final BinaryDictionary binaryDictionary = getBinaryDictionary();
- if (binaryDictionary == null) {
- return;
- }
- int token = 0;
- do {
- // TODO: We need a new API that returns *new* un-synced data.
- final BinaryDictionary.GetNextWordPropertyResult nextWordPropertyResult =
- binaryDictionary.getNextWordProperty(token);
- final WordProperty wordProperty = nextWordPropertyResult.mWordProperty;
- if (wordProperty == null) {
- break;
- }
- wordPropertyList.add(wordProperty);
- token = nextWordPropertyResult.mNextToken;
- } while (token != 0);
- result.set(wordPropertyList.toArray(new WordProperty[wordPropertyList.size()]));
- }
- });
- // TODO: Figure out the best timeout duration for this API.
- return result.get(DEFAULT_WORD_PROPERTIES_FOR_SYNC,
- TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
deleted file mode 100644
index 86c7810f4..000000000
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW;
-import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE;
-import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT;
-
-import android.text.InputType;
-import android.util.Log;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.InputTypeUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Class to hold attributes of the input field.
- */
-public final class InputAttributes {
- private final String TAG = InputAttributes.class.getSimpleName();
-
- final public String mTargetApplicationPackageName;
- final public boolean mInputTypeNoAutoCorrect;
- final public boolean mIsPasswordField;
- final public boolean mShouldShowSuggestions;
- final public boolean mApplicationSpecifiedCompletionOn;
- final public boolean mShouldInsertSpacesAutomatically;
- final public boolean mShouldShowVoiceInputKey;
- /**
- * Whether the floating gesture preview should be disabled. If true, this should override the
- * corresponding keyboard settings preference, always suppressing the floating preview text.
- * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled}
- */
- final public boolean mDisableGestureFloatingPreviewText;
- final public boolean mIsGeneralTextInput;
- final private int mInputType;
- final private EditorInfo mEditorInfo;
- final private String mPackageNameForPrivateImeOptions;
-
- public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode,
- final String packageNameForPrivateImeOptions) {
- mEditorInfo = editorInfo;
- mPackageNameForPrivateImeOptions = packageNameForPrivateImeOptions;
- mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null;
- final int inputType = null != editorInfo ? editorInfo.inputType : 0;
- final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
- mInputType = inputType;
- mIsPasswordField = InputTypeUtils.isPasswordInputType(inputType)
- || InputTypeUtils.isVisiblePasswordInputType(inputType);
- if (inputClass != InputType.TYPE_CLASS_TEXT) {
- // If we are not looking at a TYPE_CLASS_TEXT field, the following strange
- // cases may arise, so we do a couple validity checks for them. If it's a
- // TYPE_CLASS_TEXT field, these special cases cannot happen, by construction
- // of the flags.
- if (null == editorInfo) {
- Log.w(TAG, "No editor info for this field. Bug?");
- } else if (InputType.TYPE_NULL == inputType) {
- // TODO: We should honor TYPE_NULL specification.
- Log.i(TAG, "InputType.TYPE_NULL is specified");
- } else if (inputClass == 0) {
- // TODO: is this check still necessary?
- Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x"
- + " imeOptions=0x%08x", inputType, editorInfo.imeOptions));
- }
- mShouldShowSuggestions = false;
- mInputTypeNoAutoCorrect = false;
- mApplicationSpecifiedCompletionOn = false;
- mShouldInsertSpacesAutomatically = false;
- mShouldShowVoiceInputKey = false;
- mDisableGestureFloatingPreviewText = false;
- mIsGeneralTextInput = false;
- return;
- }
- // inputClass == InputType.TYPE_CLASS_TEXT
- final int variation = inputType & InputType.TYPE_MASK_VARIATION;
- final boolean flagNoSuggestions =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
- final boolean flagMultiLine =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE);
- final boolean flagAutoCorrect =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
- final boolean flagAutoComplete =
- 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
-
- // TODO: Have a helper method in InputTypeUtils
- // Make sure that passwords are not displayed in {@link SuggestionStripView}.
- final boolean shouldSuppressSuggestions = mIsPasswordField
- || InputTypeUtils.isEmailVariation(variation)
- || InputType.TYPE_TEXT_VARIATION_URI == variation
- || InputType.TYPE_TEXT_VARIATION_FILTER == variation
- || flagNoSuggestions
- || flagAutoComplete;
- mShouldShowSuggestions = !shouldSuppressSuggestions;
-
- mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType);
-
- final boolean noMicrophone = mIsPasswordField
- || InputTypeUtils.isEmailVariation(variation)
- || InputType.TYPE_TEXT_VARIATION_URI == variation
- || hasNoMicrophoneKeyOption();
- mShouldShowVoiceInputKey = !noMicrophone;
-
- mDisableGestureFloatingPreviewText = InputAttributes.inPrivateImeOptions(
- mPackageNameForPrivateImeOptions, NO_FLOATING_GESTURE_PREVIEW, editorInfo);
-
- // If it's a browser edit field and auto correct is not ON explicitly, then
- // disable auto correction, but keep suggestions on.
- // If NO_SUGGESTIONS is set, don't do prediction.
- // If it's not multiline and the autoCorrect flag is not set, then don't correct
- mInputTypeNoAutoCorrect =
- (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT && !flagAutoCorrect)
- || flagNoSuggestions
- || (!flagAutoCorrect && !flagMultiLine);
-
- mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
-
- // If we come here, inputClass is always TYPE_CLASS_TEXT
- mIsGeneralTextInput = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS != variation
- && InputType.TYPE_TEXT_VARIATION_PASSWORD != variation
- && InputType.TYPE_TEXT_VARIATION_PHONETIC != variation
- && InputType.TYPE_TEXT_VARIATION_URI != variation
- && InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD != variation
- && InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != variation
- && InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD != variation;
- }
-
- public boolean isTypeNull() {
- return InputType.TYPE_NULL == mInputType;
- }
-
- public boolean isSameInputType(final EditorInfo editorInfo) {
- return editorInfo.inputType == mInputType;
- }
-
- private boolean hasNoMicrophoneKeyOption() {
- @SuppressWarnings("deprecation")
- final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
- null, NO_MICROPHONE_COMPAT, mEditorInfo);
- final boolean noMicrophone = InputAttributes.inPrivateImeOptions(
- mPackageNameForPrivateImeOptions, NO_MICROPHONE, mEditorInfo);
- return noMicrophone || deprecatedNoMicrophone;
- }
-
- @SuppressWarnings("unused")
- private void dumpFlags(final int inputType) {
- final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
- final String inputClassString = toInputClassString(inputClass);
- final String variationString = toVariationString(
- inputClass, inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
- final String flagsString = toFlagsString(inputType & InputType.TYPE_MASK_FLAGS);
- Log.i(TAG, "Input class: " + inputClassString);
- Log.i(TAG, "Variation: " + variationString);
- Log.i(TAG, "Flags: " + flagsString);
- }
-
- private static String toInputClassString(final int inputClass) {
- switch (inputClass) {
- case InputType.TYPE_CLASS_TEXT:
- return "TYPE_CLASS_TEXT";
- case InputType.TYPE_CLASS_PHONE:
- return "TYPE_CLASS_PHONE";
- case InputType.TYPE_CLASS_NUMBER:
- return "TYPE_CLASS_NUMBER";
- case InputType.TYPE_CLASS_DATETIME:
- return "TYPE_CLASS_DATETIME";
- default:
- return String.format("unknownInputClass<0x%08x>", inputClass);
- }
- }
-
- private static String toVariationString(final int inputClass, final int variation) {
- switch (inputClass) {
- case InputType.TYPE_CLASS_TEXT:
- return toTextVariationString(variation);
- case InputType.TYPE_CLASS_NUMBER:
- return toNumberVariationString(variation);
- case InputType.TYPE_CLASS_DATETIME:
- return toDatetimeVariationString(variation);
- default:
- return "";
- }
- }
-
- private static String toTextVariationString(final int variation) {
- switch (variation) {
- case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
- return " TYPE_TEXT_VARIATION_EMAIL_ADDRESS";
- case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT:
- return "TYPE_TEXT_VARIATION_EMAIL_SUBJECT";
- case InputType.TYPE_TEXT_VARIATION_FILTER:
- return "TYPE_TEXT_VARIATION_FILTER";
- case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE:
- return "TYPE_TEXT_VARIATION_LONG_MESSAGE";
- case InputType.TYPE_TEXT_VARIATION_NORMAL:
- return "TYPE_TEXT_VARIATION_NORMAL";
- case InputType.TYPE_TEXT_VARIATION_PASSWORD:
- return "TYPE_TEXT_VARIATION_PASSWORD";
- case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
- return "TYPE_TEXT_VARIATION_PERSON_NAME";
- case InputType.TYPE_TEXT_VARIATION_PHONETIC:
- return "TYPE_TEXT_VARIATION_PHONETIC";
- case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
- return "TYPE_TEXT_VARIATION_POSTAL_ADDRESS";
- case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE:
- return "TYPE_TEXT_VARIATION_SHORT_MESSAGE";
- case InputType.TYPE_TEXT_VARIATION_URI:
- return "TYPE_TEXT_VARIATION_URI";
- case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
- return "TYPE_TEXT_VARIATION_VISIBLE_PASSWORD";
- case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT:
- return "TYPE_TEXT_VARIATION_WEB_EDIT_TEXT";
- case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS:
- return "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS";
- case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD:
- return "TYPE_TEXT_VARIATION_WEB_PASSWORD";
- default:
- return String.format("unknownVariation<0x%08x>", variation);
- }
- }
-
- private static String toNumberVariationString(final int variation) {
- switch (variation) {
- case InputType.TYPE_NUMBER_VARIATION_NORMAL:
- return "TYPE_NUMBER_VARIATION_NORMAL";
- case InputType.TYPE_NUMBER_VARIATION_PASSWORD:
- return "TYPE_NUMBER_VARIATION_PASSWORD";
- default:
- return String.format("unknownVariation<0x%08x>", variation);
- }
- }
-
- private static String toDatetimeVariationString(final int variation) {
- switch (variation) {
- case InputType.TYPE_DATETIME_VARIATION_NORMAL:
- return "TYPE_DATETIME_VARIATION_NORMAL";
- case InputType.TYPE_DATETIME_VARIATION_DATE:
- return "TYPE_DATETIME_VARIATION_DATE";
- case InputType.TYPE_DATETIME_VARIATION_TIME:
- return "TYPE_DATETIME_VARIATION_TIME";
- default:
- return String.format("unknownVariation<0x%08x>", variation);
- }
- }
-
- private static String toFlagsString(final int flags) {
- final ArrayList<String> flagsArray = new ArrayList<>();
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS))
- flagsArray.add("TYPE_TEXT_FLAG_NO_SUGGESTIONS");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_MULTI_LINE))
- flagsArray.add("TYPE_TEXT_FLAG_MULTI_LINE");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE))
- flagsArray.add("TYPE_TEXT_FLAG_IME_MULTI_LINE");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_WORDS))
- flagsArray.add("TYPE_TEXT_FLAG_CAP_WORDS");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES))
- flagsArray.add("TYPE_TEXT_FLAG_CAP_SENTENCES");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS))
- flagsArray.add("TYPE_TEXT_FLAG_CAP_CHARACTERS");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT))
- flagsArray.add("TYPE_TEXT_FLAG_AUTO_CORRECT");
- if (0 != (flags & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE))
- flagsArray.add("TYPE_TEXT_FLAG_AUTO_COMPLETE");
- return flagsArray.isEmpty() ? "" : Arrays.toString(flagsArray.toArray());
- }
-
- // Pretty print
- @Override
- public String toString() {
- return String.format(
- "%s: inputType=0x%08x%s%s%s%s%s targetApp=%s\n", getClass().getSimpleName(),
- mInputType,
- (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""),
- (mIsPasswordField ? " password" : ""),
- (mShouldShowSuggestions ? " shouldShowSuggestions" : ""),
- (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""),
- (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""),
- mTargetApplicationPackageName);
- }
-
- public static boolean inPrivateImeOptions(final String packageName, final String key,
- final EditorInfo editorInfo) {
- if (editorInfo == null) return false;
- final String findingKey = (packageName != null) ? packageName + "." + key : key;
- return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
deleted file mode 100644
index f3a8ca169..000000000
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.latin.suggestions.MoreSuggestionsView;
-import com.android.inputmethod.latin.suggestions.SuggestionStripView;
-
-public final class InputView extends FrameLayout {
- private final Rect mInputViewRect = new Rect();
- private MainKeyboardView mMainKeyboardView;
- private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
- private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
- private MotionEventForwarder<?, ?> mActiveForwarder;
-
- public InputView(final Context context, final AttributeSet attrs) {
- super(context, attrs, 0);
- }
-
- @Override
- protected void onFinishInflate() {
- final SuggestionStripView suggestionStripView =
- (SuggestionStripView)findViewById(R.id.suggestion_strip_view);
- mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view);
- mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
- mMainKeyboardView, suggestionStripView);
- mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler(
- mMainKeyboardView, suggestionStripView);
- }
-
- public void setKeyboardTopPadding(final int keyboardTopPadding) {
- mKeyboardTopPaddingForwarder.setKeyboardTopPadding(keyboardTopPadding);
- }
-
- @Override
- protected boolean dispatchHoverEvent(final MotionEvent event) {
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()
- && mMainKeyboardView.isShowingMoreKeysPanel()) {
- // With accessibility mode on, discard hover events while a more keys keyboard is shown.
- // The {@link MoreKeysKeyboard} receives hover events directly from the platform.
- return true;
- }
- return super.dispatchHoverEvent(event);
- }
-
- @Override
- public boolean onInterceptTouchEvent(final MotionEvent me) {
- final Rect rect = mInputViewRect;
- getGlobalVisibleRect(rect);
- final int index = me.getActionIndex();
- final int x = (int)me.getX(index) + rect.left;
- final int y = (int)me.getY(index) + rect.top;
-
- // The touch events that hit the top padding of keyboard should be forwarded to
- // {@link SuggestionStripView}.
- if (mKeyboardTopPaddingForwarder.onInterceptTouchEvent(x, y, me)) {
- mActiveForwarder = mKeyboardTopPaddingForwarder;
- return true;
- }
-
- // To cancel {@link MoreSuggestionsView}, we should intercept a touch event to
- // {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}.
- if (mMoreSuggestionsViewCanceler.onInterceptTouchEvent(x, y, me)) {
- mActiveForwarder = mMoreSuggestionsViewCanceler;
- return true;
- }
-
- mActiveForwarder = null;
- return false;
- }
-
- @Override
- public boolean onTouchEvent(final MotionEvent me) {
- if (mActiveForwarder == null) {
- return super.onTouchEvent(me);
- }
-
- final Rect rect = mInputViewRect;
- getGlobalVisibleRect(rect);
- final int index = me.getActionIndex();
- final int x = (int)me.getX(index) + rect.left;
- final int y = (int)me.getY(index) + rect.top;
- return mActiveForwarder.onTouchEvent(x, y, me);
- }
-
- /**
- * This class forwards series of {@link MotionEvent}s from <code>SenderView</code> to
- * <code>ReceiverView</code>.
- *
- * @param <SenderView> a {@link View} that may send a {@link MotionEvent} to <ReceiverView>.
- * @param <ReceiverView> a {@link View} that receives forwarded {@link MotionEvent} from
- * <SenderView>.
- */
- private static abstract class
- MotionEventForwarder<SenderView extends View, ReceiverView extends View> {
- protected final SenderView mSenderView;
- protected final ReceiverView mReceiverView;
-
- protected final Rect mEventSendingRect = new Rect();
- protected final Rect mEventReceivingRect = new Rect();
-
- public MotionEventForwarder(final SenderView senderView, final ReceiverView receiverView) {
- mSenderView = senderView;
- mReceiverView = receiverView;
- }
-
- // Return true if a touch event of global coordinate x, y needs to be forwarded.
- protected abstract boolean needsToForward(final int x, final int y);
-
- // Translate global x-coordinate to <code>ReceiverView</code> local coordinate.
- protected int translateX(final int x) {
- return x - mEventReceivingRect.left;
- }
-
- // Translate global y-coordinate to <code>ReceiverView</code> local coordinate.
- protected int translateY(final int y) {
- return y - mEventReceivingRect.top;
- }
-
- /**
- * Callback when a {@link MotionEvent} is forwarded.
- * @param me the motion event to be forwarded.
- */
- protected void onForwardingEvent(final MotionEvent me) {}
-
- // Returns true if a {@link MotionEvent} is needed to be forwarded to
- // <code>ReceiverView</code>. Otherwise returns false.
- public boolean onInterceptTouchEvent(final int x, final int y, final MotionEvent me) {
- // Forwards a {link MotionEvent} only if both <code>SenderView</code> and
- // <code>ReceiverView</code> are visible.
- if (mSenderView.getVisibility() != View.VISIBLE ||
- mReceiverView.getVisibility() != View.VISIBLE) {
- return false;
- }
- mSenderView.getGlobalVisibleRect(mEventSendingRect);
- if (!mEventSendingRect.contains(x, y)) {
- return false;
- }
-
- if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
- // If the down event happens in the forwarding area, successive
- // {@link MotionEvent}s should be forwarded to <code>ReceiverView</code>.
- if (needsToForward(x, y)) {
- return true;
- }
- }
-
- return false;
- }
-
- // Returns true if a {@link MotionEvent} is forwarded to <code>ReceiverView</code>.
- // Otherwise returns false.
- public boolean onTouchEvent(final int x, final int y, final MotionEvent me) {
- mReceiverView.getGlobalVisibleRect(mEventReceivingRect);
- // Translate global coordinates to <code>ReceiverView</code> local coordinates.
- me.setLocation(translateX(x), translateY(y));
- mReceiverView.dispatchTouchEvent(me);
- onForwardingEvent(me);
- return true;
- }
- }
-
- /**
- * This class forwards {@link MotionEvent}s happened in the top padding of
- * {@link MainKeyboardView} to {@link SuggestionStripView}.
- */
- private static class KeyboardTopPaddingForwarder
- extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
- private int mKeyboardTopPadding;
-
- public KeyboardTopPaddingForwarder(final MainKeyboardView mainKeyboardView,
- final SuggestionStripView suggestionStripView) {
- super(mainKeyboardView, suggestionStripView);
- }
-
- public void setKeyboardTopPadding(final int keyboardTopPadding) {
- mKeyboardTopPadding = keyboardTopPadding;
- }
-
- private boolean isInKeyboardTopPadding(final int y) {
- return y < mEventSendingRect.top + mKeyboardTopPadding;
- }
-
- @Override
- protected boolean needsToForward(final int x, final int y) {
- // Forwarding an event only when {@link MainKeyboardView} is visible.
- // Because the visibility of {@link MainKeyboardView} is controlled by its parent
- // view in {@link KeyboardSwitcher#setMainKeyboardFrame()}, we should check the
- // visibility of the parent view.
- final View mainKeyboardFrame = (View)mSenderView.getParent();
- return mainKeyboardFrame.getVisibility() == View.VISIBLE && isInKeyboardTopPadding(y);
- }
-
- @Override
- protected int translateY(final int y) {
- final int translatedY = super.translateY(y);
- if (isInKeyboardTopPadding(y)) {
- // The forwarded event should have coordinates that are inside of the target.
- return Math.min(translatedY, mEventReceivingRect.height() - 1);
- }
- return translatedY;
- }
- }
-
- /**
- * This class forwards {@link MotionEvent}s happened in the {@link MainKeyboardView} to
- * {@link SuggestionStripView} when the {@link MoreSuggestionsView} is showing.
- * {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives any event
- * outside of it.
- */
- private static class MoreSuggestionsViewCanceler
- extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
- public MoreSuggestionsViewCanceler(final MainKeyboardView mainKeyboardView,
- final SuggestionStripView suggestionStripView) {
- super(mainKeyboardView, suggestionStripView);
- }
-
- @Override
- protected boolean needsToForward(final int x, final int y) {
- return mReceiverView.isShowingMoreSuggestionPanel() && mEventSendingRect.contains(x, y);
- }
-
- @Override
- protected void onForwardingEvent(final MotionEvent me) {
- if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mReceiverView.dismissMoreSuggestionsPanel();
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
deleted file mode 100644
index 426d33e6d..000000000
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.text.TextUtils;
-
-import com.android.inputmethod.event.Event;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-
-import java.util.ArrayList;
-
-/**
- * This class encapsulates data about a word previously composed, but that has been
- * committed already. This is used for resuming suggestion, and cancel auto-correction.
- */
-public final class LastComposedWord {
- // COMMIT_TYPE_USER_TYPED_WORD is used when the word committed is the exact typed word, with
- // no hinting from the IME. It happens when some external event happens (rotating the device,
- // for example) or when auto-correction is off by settings or editor attributes.
- public static final int COMMIT_TYPE_USER_TYPED_WORD = 0;
- // COMMIT_TYPE_MANUAL_PICK is used when the user pressed a field in the suggestion strip.
- public static final int COMMIT_TYPE_MANUAL_PICK = 1;
- // COMMIT_TYPE_DECIDED_WORD is used when the IME commits the word it decided was best
- // for the current user input. It may be different from what the user typed (true auto-correct)
- // or it may be exactly what the user typed if it's in the dictionary or the IME does not have
- // enough confidence in any suggestion to auto-correct (auto-correct to typed word).
- public static final int COMMIT_TYPE_DECIDED_WORD = 2;
- // COMMIT_TYPE_CANCEL_AUTO_CORRECT is used upon committing back the old word upon cancelling
- // an auto-correction.
- public static final int COMMIT_TYPE_CANCEL_AUTO_CORRECT = 3;
-
- public static final String NOT_A_SEPARATOR = "";
-
- public final ArrayList<Event> mEvents;
- public final String mTypedWord;
- public final CharSequence mCommittedWord;
- public final String mSeparatorString;
- public final NgramContext mNgramContext;
- public final int mCapitalizedMode;
- public final InputPointers mInputPointers =
- new InputPointers(DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH);
-
- private boolean mActive;
-
- public static final LastComposedWord NOT_A_COMPOSED_WORD =
- new LastComposedWord(new ArrayList<Event>(), null, "", "",
- NOT_A_SEPARATOR, null, WordComposer.CAPS_MODE_OFF);
-
- // Warning: this is using the passed objects as is and fully expects them to be
- // immutable. Do not fiddle with their contents after you passed them to this constructor.
- public LastComposedWord(final ArrayList<Event> events,
- final InputPointers inputPointers, final String typedWord,
- final CharSequence committedWord, final String separatorString,
- final NgramContext ngramContext, final int capitalizedMode) {
- if (inputPointers != null) {
- mInputPointers.copy(inputPointers);
- }
- mTypedWord = typedWord;
- mEvents = new ArrayList<>(events);
- mCommittedWord = committedWord;
- mSeparatorString = separatorString;
- mActive = true;
- mNgramContext = ngramContext;
- mCapitalizedMode = capitalizedMode;
- }
-
- public void deactivate() {
- mActive = false;
- }
-
- public boolean canRevertCommit() {
- return mActive && !TextUtils.isEmpty(mCommittedWord) && !didCommitTypedWord();
- }
-
- private boolean didCommitTypedWord() {
- return TextUtils.equals(mTypedWord, mCommittedWord);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
deleted file mode 100644
index e68b43b39..000000000
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ /dev/null
@@ -1,2033 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import static com.android.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII;
-import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE;
-import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT;
-
-import android.Manifest.permission;
-import android.app.ActivityOptions;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.inputmethodservice.InputMethodService;
-import android.media.AudioManager;
-import android.os.Build;
-import android.os.Debug;
-import android.os.IBinder;
-import android.os.Message;
-import android.preference.PreferenceManager;
-import android.text.InputType;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
-import android.util.Printer;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodSubtype;
-
-import androidx.annotation.NonNull;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.compat.BuildCompatUtils;
-import com.android.inputmethod.compat.EditorInfoCompatUtils;
-import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
-import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils;
-import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater;
-import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
-import com.android.inputmethod.event.Event;
-import com.android.inputmethod.event.HardwareEventDecoder;
-import com.android.inputmethod.event.HardwareKeyboardEventDecoder;
-import com.android.inputmethod.event.InputTransaction;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.inputlogic.InputLogic;
-import com.android.inputmethod.latin.permissions.PermissionsManager;
-import com.android.inputmethod.latin.personalization.PersonalizationHelper;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.settings.SettingsActivity;
-import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.suggestions.SuggestionStripView;
-import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
-import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
-import com.android.inputmethod.latin.utils.ApplicationUtils;
-import com.android.inputmethod.latin.utils.DialogUtils;
-import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
-import com.android.inputmethod.latin.utils.IntentUtils;
-import com.android.inputmethod.latin.utils.JniUtils;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-import com.android.inputmethod.latin.utils.StatsUtils;
-import com.android.inputmethod.latin.utils.StatsUtilsManager;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-import com.android.inputmethod.latin.utils.ViewLayoutUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Input method implementation for Qwerty'ish keyboard.
- */
-public class LatinIME extends InputMethodService implements KeyboardActionListener,
- SuggestionStripView.Listener, SuggestionStripViewAccessor,
- DictionaryFacilitator.DictionaryInitializationListener,
- PermissionsManager.PermissionsResultCallback {
- static final String TAG = LatinIME.class.getSimpleName();
- private static final boolean TRACE = false;
-
- private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2;
- private static final int PENDING_IMS_CALLBACK_DURATION_MILLIS = 800;
- static final long DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS = TimeUnit.SECONDS.toMillis(2);
- static final long DELAY_DEALLOCATE_MEMORY_MILLIS = TimeUnit.SECONDS.toMillis(10);
-
- /**
- * A broadcast intent action to hide the software keyboard.
- */
- static final String ACTION_HIDE_SOFT_INPUT =
- "com.android.inputmethod.latin.HIDE_SOFT_INPUT";
-
- /**
- * A custom permission for external apps to send {@link #ACTION_HIDE_SOFT_INPUT}.
- */
- static final String PERMISSION_HIDE_SOFT_INPUT =
- "com.android.inputmethod.latin.HIDE_SOFT_INPUT";
-
- /**
- * The name of the scheme used by the Package Manager to warn of a new package installation,
- * replacement or removal.
- */
- private static final String SCHEME_PACKAGE = "package";
-
- final Settings mSettings;
- private final DictionaryFacilitator mDictionaryFacilitator =
- DictionaryFacilitatorProvider.getDictionaryFacilitator(
- false /* isNeededForSpellChecking */);
- final InputLogic mInputLogic = new InputLogic(this /* LatinIME */,
- this /* SuggestionStripViewAccessor */, mDictionaryFacilitator);
- // We expect to have only one decoder in almost all cases, hence the default capacity of 1.
- // If it turns out we need several, it will get grown seamlessly.
- final SparseArray<HardwareEventDecoder> mHardwareEventDecoders = new SparseArray<>(1);
-
- // TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
- private View mInputView;
- private InsetsUpdater mInsetsUpdater;
- private SuggestionStripView mSuggestionStripView;
-
- private RichInputMethodManager mRichImm;
- @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
- private final SubtypeState mSubtypeState = new SubtypeState();
- private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
- private StatsUtilsManager mStatsUtilsManager;
- // Working variable for {@link #startShowingInputView()} and
- // {@link #onEvaluateInputViewShown()}.
- private boolean mIsExecutingStartShowingInputView;
-
- // Used for re-initialize keyboard layout after onConfigurationChange.
- @Nullable private Context mDisplayContext;
-
- // Object for reacting to adding/removing a dictionary pack.
- private final BroadcastReceiver mDictionaryPackInstallReceiver =
- new DictionaryPackInstallBroadcastReceiver(this);
-
- private final BroadcastReceiver mDictionaryDumpBroadcastReceiver =
- new DictionaryDumpBroadcastReceiver(this);
-
- final static class HideSoftInputReceiver extends BroadcastReceiver {
- private final InputMethodService mIms;
-
- public HideSoftInputReceiver(InputMethodService ims) {
- mIms = ims;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (ACTION_HIDE_SOFT_INPUT.equals(action)) {
- mIms.requestHideSelf(0 /* flags */);
- } else {
- Log.e(TAG, "Unexpected intent " + intent);
- }
- }
- }
- final HideSoftInputReceiver mHideSoftInputReceiver = new HideSoftInputReceiver(this);
-
- private AlertDialog mOptionsDialog;
-
- private final boolean mIsHardwareAcceleratedDrawingEnabled;
-
- private GestureConsumer mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
-
- public final UIHandler mHandler = new UIHandler(this);
-
- public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
- private static final int MSG_UPDATE_SHIFT_STATE = 0;
- private static final int MSG_PENDING_IMS_CALLBACK = 1;
- private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
- private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3;
- private static final int MSG_RESUME_SUGGESTIONS = 4;
- private static final int MSG_REOPEN_DICTIONARIES = 5;
- private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6;
- private static final int MSG_RESET_CACHES = 7;
- private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8;
- private static final int MSG_DEALLOCATE_MEMORY = 9;
- private static final int MSG_RESUME_SUGGESTIONS_FOR_START_INPUT = 10;
- private static final int MSG_SWITCH_LANGUAGE_AUTOMATICALLY = 11;
- // Update this when adding new messages
- private static final int MSG_LAST = MSG_SWITCH_LANGUAGE_AUTOMATICALLY;
-
- private static final int ARG1_NOT_GESTURE_INPUT = 0;
- private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
- private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2;
- private static final int ARG2_UNUSED = 0;
- private static final int ARG1_TRUE = 1;
-
- private int mDelayInMillisecondsToUpdateSuggestions;
- private int mDelayInMillisecondsToUpdateShiftState;
-
- public UIHandler(@Nonnull final LatinIME ownerInstance) {
- super(ownerInstance);
- }
-
- public void onCreate() {
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme == null) {
- return;
- }
- final Resources res = latinIme.getResources();
- mDelayInMillisecondsToUpdateSuggestions = res.getInteger(
- R.integer.config_delay_in_milliseconds_to_update_suggestions);
- mDelayInMillisecondsToUpdateShiftState = res.getInteger(
- R.integer.config_delay_in_milliseconds_to_update_shift_state);
- }
-
- @Override
- public void handleMessage(final Message msg) {
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme == null) {
- return;
- }
- final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
- switch (msg.what) {
- case MSG_UPDATE_SUGGESTION_STRIP:
- cancelUpdateSuggestionStrip();
- latinIme.mInputLogic.performUpdateSuggestionStripSync(
- latinIme.mSettings.getCurrent(), msg.arg1 /* inputStyle */);
- break;
- case MSG_UPDATE_SHIFT_STATE:
- switcher.requestUpdatingShiftState(latinIme.getCurrentAutoCapsState(),
- latinIme.getCurrentRecapitalizeState());
- break;
- case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
- if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) {
- final SuggestedWords suggestedWords = (SuggestedWords) msg.obj;
- latinIme.showSuggestionStrip(suggestedWords);
- } else {
- latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj,
- msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
- }
- break;
- case MSG_RESUME_SUGGESTIONS:
- latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
- latinIme.mSettings.getCurrent(), false /* forStartInput */,
- latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId());
- break;
- case MSG_RESUME_SUGGESTIONS_FOR_START_INPUT:
- latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
- latinIme.mSettings.getCurrent(), true /* forStartInput */,
- latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId());
- break;
- case MSG_REOPEN_DICTIONARIES:
- // We need to re-evaluate the currently composing word in case the script has
- // changed.
- postWaitForDictionaryLoad();
- latinIme.resetDictionaryFacilitatorIfNecessary();
- break;
- case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED:
- final SuggestedWords suggestedWords = (SuggestedWords) msg.obj;
- latinIme.mInputLogic.onUpdateTailBatchInputCompleted(
- latinIme.mSettings.getCurrent(),
- suggestedWords, latinIme.mKeyboardSwitcher);
- latinIme.onTailBatchInputResultShown(suggestedWords);
- break;
- case MSG_RESET_CACHES:
- final SettingsValues settingsValues = latinIme.mSettings.getCurrent();
- if (latinIme.mInputLogic.retryResetCachesAndReturnSuccess(
- msg.arg1 == ARG1_TRUE /* tryResumeSuggestions */,
- msg.arg2 /* remainingTries */, this /* handler */)) {
- // If we were able to reset the caches, then we can reload the keyboard.
- // Otherwise, we'll do it when we can.
- latinIme.mKeyboardSwitcher.loadKeyboard(latinIme.getCurrentInputEditorInfo(),
- settingsValues, latinIme.getCurrentAutoCapsState(),
- latinIme.getCurrentRecapitalizeState());
- }
- break;
- case MSG_WAIT_FOR_DICTIONARY_LOAD:
- Log.i(TAG, "Timeout waiting for dictionary load");
- break;
- case MSG_DEALLOCATE_MEMORY:
- latinIme.deallocateMemory();
- break;
- case MSG_SWITCH_LANGUAGE_AUTOMATICALLY:
- latinIme.switchLanguage((InputMethodSubtype)msg.obj);
- break;
- }
- }
-
- public void postUpdateSuggestionStrip(final int inputStyle) {
- sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP, inputStyle,
- 0 /* ignored */), mDelayInMillisecondsToUpdateSuggestions);
- }
-
- public void postReopenDictionaries() {
- sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES));
- }
-
- private void postResumeSuggestionsInternal(final boolean shouldDelay,
- final boolean forStartInput) {
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme == null) {
- return;
- }
- if (!latinIme.mSettings.getCurrent().isSuggestionsEnabledPerUserSettings()) {
- return;
- }
- removeMessages(MSG_RESUME_SUGGESTIONS);
- removeMessages(MSG_RESUME_SUGGESTIONS_FOR_START_INPUT);
- final int message = forStartInput ? MSG_RESUME_SUGGESTIONS_FOR_START_INPUT
- : MSG_RESUME_SUGGESTIONS;
- if (shouldDelay) {
- sendMessageDelayed(obtainMessage(message),
- mDelayInMillisecondsToUpdateSuggestions);
- } else {
- sendMessage(obtainMessage(message));
- }
- }
-
- public void postResumeSuggestions(final boolean shouldDelay) {
- postResumeSuggestionsInternal(shouldDelay, false /* forStartInput */);
- }
-
- public void postResumeSuggestionsForStartInput(final boolean shouldDelay) {
- postResumeSuggestionsInternal(shouldDelay, true /* forStartInput */);
- }
-
- public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
- removeMessages(MSG_RESET_CACHES);
- sendMessage(obtainMessage(MSG_RESET_CACHES, tryResumeSuggestions ? 1 : 0,
- remainingTries, null));
- }
-
- public void postWaitForDictionaryLoad() {
- sendMessageDelayed(obtainMessage(MSG_WAIT_FOR_DICTIONARY_LOAD),
- DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS);
- }
-
- public void cancelWaitForDictionaryLoad() {
- removeMessages(MSG_WAIT_FOR_DICTIONARY_LOAD);
- }
-
- public boolean hasPendingWaitForDictionaryLoad() {
- return hasMessages(MSG_WAIT_FOR_DICTIONARY_LOAD);
- }
-
- public void cancelUpdateSuggestionStrip() {
- removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
- }
-
- public boolean hasPendingUpdateSuggestions() {
- return hasMessages(MSG_UPDATE_SUGGESTION_STRIP);
- }
-
- public boolean hasPendingReopenDictionaries() {
- return hasMessages(MSG_REOPEN_DICTIONARIES);
- }
-
- public void postUpdateShiftState() {
- removeMessages(MSG_UPDATE_SHIFT_STATE);
- sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE),
- mDelayInMillisecondsToUpdateShiftState);
- }
-
- public void postDeallocateMemory() {
- sendMessageDelayed(obtainMessage(MSG_DEALLOCATE_MEMORY),
- DELAY_DEALLOCATE_MEMORY_MILLIS);
- }
-
- public void cancelDeallocateMemory() {
- removeMessages(MSG_DEALLOCATE_MEMORY);
- }
-
- public boolean hasPendingDeallocateMemory() {
- return hasMessages(MSG_DEALLOCATE_MEMORY);
- }
-
- @UsedForTesting
- public void removeAllMessages() {
- for (int i = 0; i <= MSG_LAST; ++i) {
- removeMessages(i);
- }
- }
-
- public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
- final boolean dismissGestureFloatingPreviewText) {
- removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
- final int arg1 = dismissGestureFloatingPreviewText
- ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT
- : ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT;
- obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1,
- ARG2_UNUSED, suggestedWords).sendToTarget();
- }
-
- public void showSuggestionStrip(final SuggestedWords suggestedWords) {
- removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
- obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP,
- ARG1_NOT_GESTURE_INPUT, ARG2_UNUSED, suggestedWords).sendToTarget();
- }
-
- public void showTailBatchInputResult(final SuggestedWords suggestedWords) {
- obtainMessage(MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED, suggestedWords).sendToTarget();
- }
-
- public void postSwitchLanguage(final InputMethodSubtype subtype) {
- obtainMessage(MSG_SWITCH_LANGUAGE_AUTOMATICALLY, subtype).sendToTarget();
- }
-
- // Working variables for the following methods.
- private boolean mIsOrientationChanging;
- private boolean mPendingSuccessiveImsCallback;
- private boolean mHasPendingStartInput;
- private boolean mHasPendingFinishInputView;
- private boolean mHasPendingFinishInput;
- private EditorInfo mAppliedEditorInfo;
-
- public void startOrientationChanging() {
- removeMessages(MSG_PENDING_IMS_CALLBACK);
- resetPendingImsCallback();
- mIsOrientationChanging = true;
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme == null) {
- return;
- }
- if (latinIme.isInputViewShown()) {
- latinIme.mKeyboardSwitcher.saveKeyboardState();
- }
- }
-
- private void resetPendingImsCallback() {
- mHasPendingFinishInputView = false;
- mHasPendingFinishInput = false;
- mHasPendingStartInput = false;
- }
-
- private void executePendingImsCallback(final LatinIME latinIme, final EditorInfo editorInfo,
- boolean restarting) {
- if (mHasPendingFinishInputView) {
- latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
- }
- if (mHasPendingFinishInput) {
- latinIme.onFinishInputInternal();
- }
- if (mHasPendingStartInput) {
- latinIme.onStartInputInternal(editorInfo, restarting);
- }
- resetPendingImsCallback();
- }
-
- public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
- if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
- // Typically this is the second onStartInput after orientation changed.
- mHasPendingStartInput = true;
- } else {
- if (mIsOrientationChanging && restarting) {
- // This is the first onStartInput after orientation changed.
- mIsOrientationChanging = false;
- mPendingSuccessiveImsCallback = true;
- }
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme != null) {
- executePendingImsCallback(latinIme, editorInfo, restarting);
- latinIme.onStartInputInternal(editorInfo, restarting);
- }
- }
- }
-
- public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
- if (hasMessages(MSG_PENDING_IMS_CALLBACK)
- && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
- // Typically this is the second onStartInputView after orientation changed.
- resetPendingImsCallback();
- } else {
- if (mPendingSuccessiveImsCallback) {
- // This is the first onStartInputView after orientation changed.
- mPendingSuccessiveImsCallback = false;
- resetPendingImsCallback();
- sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
- PENDING_IMS_CALLBACK_DURATION_MILLIS);
- }
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme != null) {
- executePendingImsCallback(latinIme, editorInfo, restarting);
- latinIme.onStartInputViewInternal(editorInfo, restarting);
- mAppliedEditorInfo = editorInfo;
- }
- cancelDeallocateMemory();
- }
- }
-
- public void onFinishInputView(final boolean finishingInput) {
- if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
- // Typically this is the first onFinishInputView after orientation changed.
- mHasPendingFinishInputView = true;
- } else {
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme != null) {
- latinIme.onFinishInputViewInternal(finishingInput);
- mAppliedEditorInfo = null;
- }
- if (!hasPendingDeallocateMemory()) {
- postDeallocateMemory();
- }
- }
- }
-
- public void onFinishInput() {
- if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
- // Typically this is the first onFinishInput after orientation changed.
- mHasPendingFinishInput = true;
- } else {
- final LatinIME latinIme = getOwnerInstance();
- if (latinIme != null) {
- executePendingImsCallback(latinIme, null, false);
- latinIme.onFinishInputInternal();
- }
- }
- }
- }
-
- static final class SubtypeState {
- private InputMethodSubtype mLastActiveSubtype;
- private boolean mCurrentSubtypeHasBeenUsed;
-
- public void setCurrentSubtypeHasBeenUsed() {
- mCurrentSubtypeHasBeenUsed = true;
- }
-
- public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) {
- final InputMethodSubtype currentSubtype = richImm.getInputMethodManager()
- .getCurrentInputMethodSubtype();
- final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype;
- final boolean currentSubtypeHasBeenUsed = mCurrentSubtypeHasBeenUsed;
- if (currentSubtypeHasBeenUsed) {
- mLastActiveSubtype = currentSubtype;
- mCurrentSubtypeHasBeenUsed = false;
- }
- if (currentSubtypeHasBeenUsed
- && richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype)
- && !currentSubtype.equals(lastActiveSubtype)) {
- richImm.setInputMethodAndSubtype(token, lastActiveSubtype);
- return;
- }
- richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
- }
- }
-
- // Loading the native library eagerly to avoid unexpected UnsatisfiedLinkError at the initial
- // JNI call as much as possible.
- static {
- JniUtils.loadNativeLibrary();
- }
-
- public LatinIME() {
- super();
- mSettings = Settings.getInstance();
- mKeyboardSwitcher = KeyboardSwitcher.getInstance();
- mStatsUtilsManager = StatsUtilsManager.getInstance();
- mIsHardwareAcceleratedDrawingEnabled =
- InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
- Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled);
- }
-
- @Override
- public void onCreate() {
- Settings.init(this);
- DebugFlags.init(PreferenceManager.getDefaultSharedPreferences(this));
- RichInputMethodManager.init(this);
- mRichImm = RichInputMethodManager.getInstance();
- AudioAndHapticFeedbackManager.init(this);
- AccessibilityUtils.init(this);
- mStatsUtilsManager.onCreate(this /* context */, mDictionaryFacilitator);
- final WindowManager wm = getSystemService(WindowManager.class);
- mDisplayContext = getDisplayContext();
- KeyboardSwitcher.init(this);
- super.onCreate();
-
- mHandler.onCreate();
-
- // TODO: Resolve mutual dependencies of {@link #loadSettings()} and
- // {@link #resetDictionaryFacilitatorIfNecessary()}.
- loadSettings();
- resetDictionaryFacilitatorIfNecessary();
-
- // Register to receive ringer mode change.
- final IntentFilter filter = new IntentFilter();
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- registerReceiver(mRingerModeChangeReceiver, filter);
-
- // Register to receive installation and removal of a dictionary pack.
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme(SCHEME_PACKAGE);
- registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
-
- final IntentFilter newDictFilter = new IntentFilter();
- newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- registerReceiver(mDictionaryPackInstallReceiver, newDictFilter,
- Context.RECEIVER_NOT_EXPORTED);
- } else {
- registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
- }
-
- final IntentFilter dictDumpFilter = new IntentFilter();
- dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter,
- Context.RECEIVER_NOT_EXPORTED);
- } else {
- registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter);
- }
-
- final IntentFilter hideSoftInputFilter = new IntentFilter();
- hideSoftInputFilter.addAction(ACTION_HIDE_SOFT_INPUT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- registerReceiver(mHideSoftInputReceiver, hideSoftInputFilter,
- PERMISSION_HIDE_SOFT_INPUT, null /* scheduler */, Context.RECEIVER_EXPORTED);
- } else {
- registerReceiver(mHideSoftInputReceiver, hideSoftInputFilter,
- PERMISSION_HIDE_SOFT_INPUT, null /* scheduler */);
- }
-
- StatsUtils.onCreate(mSettings.getCurrent(), mRichImm);
- }
-
- // Has to be package-visible for unit tests
- @UsedForTesting
- void loadSettings() {
- final Locale locale = mRichImm.getCurrentSubtypeLocale();
- final EditorInfo editorInfo = getCurrentInputEditorInfo();
- final InputAttributes inputAttributes = new InputAttributes(
- editorInfo, isFullscreenMode(), getPackageName());
- mSettings.loadSettings(this, locale, inputAttributes);
- final SettingsValues currentSettingsValues = mSettings.getCurrent();
- AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues);
- // This method is called on startup and language switch, before the new layout has
- // been displayed. Opening dictionaries never affects responsivity as dictionaries are
- // asynchronously loaded.
- if (!mHandler.hasPendingReopenDictionaries()) {
- resetDictionaryFacilitator(locale);
- }
- refreshPersonalizationDictionarySession(currentSettingsValues);
- resetDictionaryFacilitatorIfNecessary();
- mStatsUtilsManager.onLoadSettings(this /* context */, currentSettingsValues);
- }
-
- private void refreshPersonalizationDictionarySession(
- final SettingsValues currentSettingsValues) {
- if (!currentSettingsValues.mUsePersonalizedDicts) {
- // Remove user history dictionaries.
- PersonalizationHelper.removeAllUserHistoryDictionaries(this);
- mDictionaryFacilitator.clearUserHistoryDictionary(this);
- }
- }
-
- // Note that this method is called from a non-UI thread.
- @Override
- public void onUpdateMainDictionaryAvailability(final boolean isMainDictionaryAvailable) {
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (mainKeyboardView != null) {
- mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
- }
- if (mHandler.hasPendingWaitForDictionaryLoad()) {
- mHandler.cancelWaitForDictionaryLoad();
- mHandler.postResumeSuggestions(false /* shouldDelay */);
- }
- }
-
- void resetDictionaryFacilitatorIfNecessary() {
- final Locale subtypeSwitcherLocale = mRichImm.getCurrentSubtypeLocale();
- final Locale subtypeLocale;
- if (subtypeSwitcherLocale == null) {
- // This happens in very rare corner cases - for example, immediately after a switch
- // to LatinIME has been requested, about a frame later another switch happens. In this
- // case, we are about to go down but we still don't know it, however the system tells
- // us there is no current subtype.
- Log.e(TAG, "System is reporting no current subtype.");
- subtypeLocale = getResources().getConfiguration().locale;
- } else {
- subtypeLocale = subtypeSwitcherLocale;
- }
- if (mDictionaryFacilitator.isForLocale(subtypeLocale)
- && mDictionaryFacilitator.isForAccount(mSettings.getCurrent().mAccount)) {
- return;
- }
- resetDictionaryFacilitator(subtypeLocale);
- }
-
- /**
- * Reset the facilitator by loading dictionaries for the given locale and
- * the current settings values.
- *
- * @param locale the locale
- */
- // TODO: make sure the current settings always have the right locales, and read from them.
- private void resetDictionaryFacilitator(final Locale locale) {
- final SettingsValues settingsValues = mSettings.getCurrent();
- mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
- settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
- false /* forceReloadMainDictionary */,
- settingsValues.mAccount, "" /* dictNamePrefix */,
- this /* DictionaryInitializationListener */);
- if (settingsValues.mAutoCorrectionEnabledPerUserSettings) {
- mInputLogic.mSuggest.setAutoCorrectionThreshold(
- settingsValues.mAutoCorrectionThreshold);
- }
- mInputLogic.mSuggest.setPlausibilityThreshold(settingsValues.mPlausibilityThreshold);
- }
-
- /**
- * Reset suggest by loading the main dictionary of the current locale.
- */
- /* package private */ void resetSuggestMainDict() {
- final SettingsValues settingsValues = mSettings.getCurrent();
- mDictionaryFacilitator.resetDictionaries(this /* context */,
- mDictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict,
- settingsValues.mUsePersonalizedDicts,
- true /* forceReloadMainDictionary */,
- settingsValues.mAccount, "" /* dictNamePrefix */,
- this /* DictionaryInitializationListener */);
- }
-
- @Override
- public void onDestroy() {
- mDictionaryFacilitator.closeDictionaries();
- mSettings.onDestroy();
- unregisterReceiver(mHideSoftInputReceiver);
- unregisterReceiver(mRingerModeChangeReceiver);
- unregisterReceiver(mDictionaryPackInstallReceiver);
- unregisterReceiver(mDictionaryDumpBroadcastReceiver);
- mStatsUtilsManager.onDestroy(this /* context */);
- super.onDestroy();
- }
-
- @UsedForTesting
- public void recycle() {
- unregisterReceiver(mDictionaryPackInstallReceiver);
- unregisterReceiver(mDictionaryDumpBroadcastReceiver);
- unregisterReceiver(mRingerModeChangeReceiver);
- mInputLogic.recycle();
- }
-
- private boolean isImeSuppressedByHardwareKeyboard() {
- final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
- return !onEvaluateInputViewShown() && switcher.isImeSuppressedByHardwareKeyboard(
- mSettings.getCurrent(), switcher.getKeyboardSwitchState());
- }
-
- @Override
- public void onConfigurationChanged(final Configuration conf) {
- SettingsValues settingsValues = mSettings.getCurrent();
- if (settingsValues.mDisplayOrientation != conf.orientation) {
- mHandler.startOrientationChanging();
- mInputLogic.onOrientationChange(mSettings.getCurrent());
- }
- if (settingsValues.mHasHardwareKeyboard != Settings.readHasHardwareKeyboard(conf)) {
- // If the state of having a hardware keyboard changed, then we want to reload the
- // settings to adjust for that.
- // TODO: we should probably do this unconditionally here, rather than only when we
- // have a change in hardware keyboard configuration.
- loadSettings();
- settingsValues = mSettings.getCurrent();
- if (isImeSuppressedByHardwareKeyboard()) {
- // We call cleanupInternalStateForFinishInput() because it's the right thing to do;
- // however, it seems at the moment the framework is passing us a seemingly valid
- // but actually non-functional InputConnection object. So if this bug ever gets
- // fixed we'll be able to remove the composition, but until it is this code is
- // actually not doing much.
- cleanupInternalStateForFinishInput();
- }
- }
- super.onConfigurationChanged(conf);
- }
-
- @Override
- public void onInitializeInterface() {
- mDisplayContext = getDisplayContext();
- mKeyboardSwitcher.updateKeyboardTheme(mDisplayContext);
- }
-
- /**
- * Returns the context object whose resources are adjusted to match the metrics of the display.
- *
- * Note that before {@link android.os.Build.VERSION_CODES#KITKAT}, there is no way to support
- * multi-display scenarios, so the context object will just return the IME context itself.
- *
- * With initiating multi-display APIs from {@link android.os.Build.VERSION_CODES#KITKAT}, the
- * context object has to return with re-creating the display context according the metrics
- * of the display in runtime.
- *
- * Starts from {@link android.os.Build.VERSION_CODES#S_V2}, the returning context object has
- * became to IME context self since it ends up capable of updating its resources internally.
- *
- * @see android.content.Context#createDisplayContext(Display)
- */
- private @NonNull Context getDisplayContext() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- // createDisplayContext is not available.
- return this;
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2) {
- // IME context sources is now managed by WindowProviderService from Android 12L.
- return this;
- }
- // An issue in Q that non-activity components Resources / DisplayMetrics in
- // Context doesn't well updated when the IME window moving to external display.
- // Currently we do a workaround is to create new display context directly and re-init
- // keyboard layout with this context.
- final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- return createDisplayContext(wm.getDefaultDisplay());
- }
-
- @Override
- public View onCreateInputView() {
- StatsUtils.onCreateInputView();
- return mKeyboardSwitcher.onCreateInputView(mDisplayContext,
- mIsHardwareAcceleratedDrawingEnabled);
- }
-
- @Override
- public void setInputView(final View view) {
- super.setInputView(view);
- mInputView = view;
- mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view);
- updateSoftInputWindowLayoutParameters();
- mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
- if (hasSuggestionStripView()) {
- mSuggestionStripView.setListener(this, view);
- }
- }
-
- @Override
- public void setCandidatesView(final View view) {
- // To ensure that CandidatesView will never be set.
- }
-
- @Override
- public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
- mHandler.onStartInput(editorInfo, restarting);
- }
-
- @Override
- public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
- mHandler.onStartInputView(editorInfo, restarting);
- mStatsUtilsManager.onStartInputView();
- }
-
- @Override
- public void onFinishInputView(final boolean finishingInput) {
- StatsUtils.onFinishInputView();
- mHandler.onFinishInputView(finishingInput);
- mStatsUtilsManager.onFinishInputView();
- mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
- }
-
- @Override
- public void onFinishInput() {
- mHandler.onFinishInput();
- }
-
- @Override
- public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
- // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
- // is not guaranteed. It may even be called at the same time on a different thread.
- InputMethodSubtype oldSubtype = mRichImm.getCurrentSubtype().getRawSubtype();
- StatsUtils.onSubtypeChanged(oldSubtype, subtype);
- mRichImm.onSubtypeChanged(subtype);
- mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype),
- mSettings.getCurrent());
- loadKeyboard();
- }
-
- void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
- super.onStartInput(editorInfo, restarting);
-
- // If the primary hint language does not match the current subtype language, then try
- // to switch to the primary hint language.
- // TODO: Support all the locales in EditorInfo#hintLocales.
- final Locale primaryHintLocale = EditorInfoCompatUtils.getPrimaryHintLocale(editorInfo);
- if (primaryHintLocale == null) {
- return;
- }
- final InputMethodSubtype newSubtype = mRichImm.findSubtypeByLocale(primaryHintLocale);
- if (newSubtype == null || newSubtype.equals(mRichImm.getCurrentSubtype().getRawSubtype())) {
- return;
- }
- mHandler.postSwitchLanguage(newSubtype);
- }
-
- @SuppressWarnings("deprecation")
- void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
- super.onStartInputView(editorInfo, restarting);
-
- mDictionaryFacilitator.onStartInput();
- // Switch to the null consumer to handle cases leading to early exit below, for which we
- // also wouldn't be consuming gesture data.
- mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
- mRichImm.refreshSubtypeCaches();
- final KeyboardSwitcher switcher = mKeyboardSwitcher;
- switcher.updateKeyboardTheme(mDisplayContext);
- final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
- // If we are starting input in a different text field from before, we'll have to reload
- // settings, so currentSettingsValues can't be final.
- SettingsValues currentSettingsValues = mSettings.getCurrent();
-
- if (editorInfo == null) {
- Log.e(TAG, "Null EditorInfo in onStartInputView()");
- if (DebugFlags.DEBUG_ENABLED) {
- throw new NullPointerException("Null EditorInfo in onStartInputView()");
- }
- return;
- }
- if (DebugFlags.DEBUG_ENABLED) {
- Log.d(TAG, "onStartInputView: editorInfo:"
- + String.format("inputType=0x%08x imeOptions=0x%08x",
- editorInfo.inputType, editorInfo.imeOptions));
- Log.d(TAG, "All caps = "
- + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0)
- + ", sentence caps = "
- + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0)
- + ", word caps = "
- + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
- }
- Log.i(TAG, "Starting input. Cursor position = "
- + editorInfo.initialSelStart + "," + editorInfo.initialSelEnd);
- // TODO: Consolidate these checks with {@link InputAttributes}.
- if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
- Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions);
- Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
- }
- if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) {
- Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions);
- Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
- }
-
- // In landscape mode, this method gets called without the input view being created.
- if (mainKeyboardView == null) {
- return;
- }
-
- // Update to a gesture consumer with the current editor and IME state.
- mGestureConsumer = GestureConsumer.newInstance(editorInfo,
- mInputLogic.getPrivateCommandPerformer(),
- mRichImm.getCurrentSubtypeLocale(),
- switcher.getKeyboard());
-
- // Forward this event to the accessibility utilities, if enabled.
- final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
- if (accessUtils.isTouchExplorationEnabled()) {
- accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
- }
-
- final boolean inputTypeChanged = !currentSettingsValues.isSameInputType(editorInfo);
- final boolean isDifferentTextField = !restarting || inputTypeChanged;
-
- StatsUtils.onStartInputView(editorInfo.inputType,
- Settings.getInstance().getCurrent().mDisplayOrientation,
- !isDifferentTextField);
-
- // The EditorInfo might have a flag that affects fullscreen mode.
- // Note: This call should be done by InputMethodService?
- updateFullscreenMode();
-
- // ALERT: settings have not been reloaded and there is a chance they may be stale.
- // In the practice, if it is, we should have gotten onConfigurationChanged so it should
- // be fine, but this is horribly confusing and must be fixed AS SOON AS POSSIBLE.
-
- // In some cases the input connection has not been reset yet and we can't access it. In
- // this case we will need to call loadKeyboard() later, when it's accessible, so that we
- // can go into the correct mode, so we need to do some housekeeping here.
- final boolean needToCallLoadKeyboardLater;
- final Suggest suggest = mInputLogic.mSuggest;
- if (!isImeSuppressedByHardwareKeyboard()) {
- // The app calling setText() has the effect of clearing the composing
- // span, so we should reset our state unconditionally, even if restarting is true.
- // We also tell the input logic about the combining rules for the current subtype, so
- // it can adjust its combiners if needed.
- mInputLogic.startInput(mRichImm.getCombiningRulesExtraValueOfCurrentSubtype(),
- currentSettingsValues);
-
- resetDictionaryFacilitatorIfNecessary();
-
- // TODO[IL]: Can the following be moved to InputLogic#startInput?
- if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
- editorInfo.initialSelStart, editorInfo.initialSelEnd,
- false /* shouldFinishComposition */)) {
- // Sometimes, while rotating, for some reason the framework tells the app we are not
- // connected to it and that means we can't refresh the cache. In this case, schedule
- // a refresh later.
- // We try resetting the caches up to 5 times before giving up.
- mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
- // mLastSelection{Start,End} are reset later in this method, no need to do it here
- needToCallLoadKeyboardLater = true;
- } else {
- // When rotating, and when input is starting again in a field from where the focus
- // didn't move (the keyboard having been closed with the back key),
- // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to
- // work around this bug.
- mInputLogic.mConnection.tryFixLyingCursorPosition();
- mHandler.postResumeSuggestionsForStartInput(true /* shouldDelay */);
- needToCallLoadKeyboardLater = false;
- }
- } else {
- // If we have a hardware keyboard we don't need to call loadKeyboard later anyway.
- needToCallLoadKeyboardLater = false;
- }
-
- if (isDifferentTextField ||
- !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) {
- loadSettings();
- }
- if (isDifferentTextField) {
- mainKeyboardView.closing();
- currentSettingsValues = mSettings.getCurrent();
-
- if (currentSettingsValues.mAutoCorrectionEnabledPerUserSettings) {
- suggest.setAutoCorrectionThreshold(
- currentSettingsValues.mAutoCorrectionThreshold);
- }
- suggest.setPlausibilityThreshold(currentSettingsValues.mPlausibilityThreshold);
-
- switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- if (needToCallLoadKeyboardLater) {
- // If we need to call loadKeyboard again later, we need to save its state now. The
- // later call will be done in #retryResetCaches.
- switcher.saveKeyboardState();
- }
- } else if (restarting) {
- // TODO: Come up with a more comprehensive way to reset the keyboard layout when
- // a keyboard layout set doesn't get reloaded in this method.
- switcher.resetKeyboardStateToAlphabet(getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- // In apps like Talk, we come here when the text is sent and the field gets emptied and
- // we need to re-evaluate the shift state, but not the whole layout which would be
- // disruptive.
- // Space state must be updated before calling updateShiftState
- switcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- }
- // This will set the punctuation suggestions if next word suggestion is off;
- // otherwise it will clear the suggestion strip.
- setNeutralSuggestionStrip();
-
- mHandler.cancelUpdateSuggestionStrip();
-
- mainKeyboardView.setMainDictionaryAvailability(
- mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary());
- mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn,
- currentSettingsValues.mKeyPreviewPopupDismissDelay);
- mainKeyboardView.setSlidingKeyInputPreviewEnabled(
- currentSettingsValues.mSlidingKeyInputPreviewEnabled);
- mainKeyboardView.setGestureHandlingEnabledByUser(
- currentSettingsValues.mGestureInputEnabled,
- currentSettingsValues.mGestureTrailEnabled,
- currentSettingsValues.mGestureFloatingPreviewTextEnabled);
-
- if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
- }
-
- @Override
- public void onWindowShown() {
- super.onWindowShown();
- setNavigationBarVisibility(isInputViewShown());
- }
-
- @Override
- public void onWindowHidden() {
- super.onWindowHidden();
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (mainKeyboardView != null) {
- mainKeyboardView.closing();
- }
- setNavigationBarVisibility(false);
- }
-
- void onFinishInputInternal() {
- super.onFinishInput();
-
- mDictionaryFacilitator.onFinishInput(this);
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (mainKeyboardView != null) {
- mainKeyboardView.closing();
- }
- }
-
- void onFinishInputViewInternal(final boolean finishingInput) {
- super.onFinishInputView(finishingInput);
- cleanupInternalStateForFinishInput();
- }
-
- private void cleanupInternalStateForFinishInput() {
- // Remove pending messages related to update suggestions
- mHandler.cancelUpdateSuggestionStrip();
- // Should do the following in onFinishInputInternal but until JB MR2 it's not called :(
- mInputLogic.finishInput();
- }
-
- protected void deallocateMemory() {
- mKeyboardSwitcher.deallocateMemory();
- }
-
- @Override
- public void onUpdateSelection(final int oldSelStart, final int oldSelEnd,
- final int newSelStart, final int newSelEnd,
- final int composingSpanStart, final int composingSpanEnd) {
- super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
- composingSpanStart, composingSpanEnd);
- if (DebugFlags.DEBUG_ENABLED) {
- Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd
- + ", nss=" + newSelStart + ", nse=" + newSelEnd
- + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd);
- }
-
- // This call happens whether our view is displayed or not, but if it's not then we should
- // not attempt recorrection. This is true even with a hardware keyboard connected: if the
- // view is not displayed we have no means of showing suggestions anyway, and if it is then
- // we want to show suggestions anyway.
- final SettingsValues settingsValues = mSettings.getCurrent();
- if (isInputViewShown()
- && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
- settingsValues)) {
- mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- }
- }
-
- /**
- * This is called when the user has clicked on the extracted text view,
- * when running in fullscreen mode. The default implementation hides
- * the suggestions view when this happens, but only if the extracted text
- * editor has a vertical scroll bar because its text doesn't fit.
- * Here we override the behavior due to the possibility that a re-correction could
- * cause the suggestions strip to disappear and re-appear.
- */
- @Override
- public void onExtractedTextClicked() {
- if (mSettings.getCurrent().needsToLookupSuggestions()) {
- return;
- }
-
- super.onExtractedTextClicked();
- }
-
- /**
- * This is called when the user has performed a cursor movement in the
- * extracted text view, when it is running in fullscreen mode. The default
- * implementation hides the suggestions view when a vertical movement
- * happens, but only if the extracted text editor has a vertical scroll bar
- * because its text doesn't fit.
- * Here we override the behavior due to the possibility that a re-correction could
- * cause the suggestions strip to disappear and re-appear.
- */
- @Override
- public void onExtractedCursorMovement(final int dx, final int dy) {
- if (mSettings.getCurrent().needsToLookupSuggestions()) {
- return;
- }
-
- super.onExtractedCursorMovement(dx, dy);
- }
-
- @Override
- public void hideWindow() {
- mKeyboardSwitcher.onHideWindow();
-
- if (TRACE) Debug.stopMethodTracing();
- if (isShowingOptionDialog()) {
- mOptionsDialog.dismiss();
- mOptionsDialog = null;
- }
- super.hideWindow();
- }
-
- @Override
- public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
- if (DebugFlags.DEBUG_ENABLED) {
- Log.i(TAG, "Received completions:");
- if (applicationSpecifiedCompletions != null) {
- for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
- Log.i(TAG, " #" + i + ": " + applicationSpecifiedCompletions[i]);
- }
- }
- }
- if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) {
- return;
- }
- // If we have an update request in flight, we need to cancel it so it does not override
- // these completions.
- mHandler.cancelUpdateSuggestionStrip();
- if (applicationSpecifiedCompletions == null) {
- setNeutralSuggestionStrip();
- return;
- }
-
- final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
- SuggestedWords.getFromApplicationSpecifiedCompletions(
- applicationSpecifiedCompletions);
- final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords,
- null /* rawSuggestions */,
- null /* typedWord */,
- false /* typedWordValid */,
- false /* willAutoCorrect */,
- false /* isObsoleteSuggestions */,
- SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */,
- SuggestedWords.NOT_A_SEQUENCE_NUMBER);
- // When in fullscreen mode, show completions generated by the application forcibly
- setSuggestedWords(suggestedWords);
- }
-
- @Override
- public void onComputeInsets(final InputMethodService.Insets outInsets) {
- super.onComputeInsets(outInsets);
- // This method may be called before {@link #setInputView(View)}.
- if (mInputView == null) {
- return;
- }
- final SettingsValues settingsValues = mSettings.getCurrent();
- final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
- if (visibleKeyboardView == null || !hasSuggestionStripView()) {
- return;
- }
- final int inputHeight = mInputView.getHeight();
- if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
- // If there is a hardware keyboard and a visible software keyboard view has been hidden,
- // no visual element will be shown on the screen.
- outInsets.contentTopInsets = inputHeight;
- outInsets.visibleTopInsets = inputHeight;
- mInsetsUpdater.setInsets(outInsets);
- return;
- }
- final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes()
- && mSuggestionStripView.getVisibility() == View.VISIBLE)
- ? mSuggestionStripView.getHeight() : 0;
- final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight;
- mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
- // Need to set expanded touchable region only if a keyboard view is being shown.
- if (visibleKeyboardView.isShown()) {
- final int touchLeft = 0;
- final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
- final int touchRight = visibleKeyboardView.getWidth();
- final int touchBottom = inputHeight;
- outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
- outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
- }
- outInsets.contentTopInsets = visibleTopY;
- outInsets.visibleTopInsets = visibleTopY;
- mInsetsUpdater.setInsets(outInsets);
- }
-
- public void startShowingInputView(final boolean needsToLoadKeyboard) {
- mIsExecutingStartShowingInputView = true;
- // This {@link #showWindow(boolean)} will eventually call back
- // {@link #onEvaluateInputViewShown()}.
- showWindow(true /* showInput */);
- mIsExecutingStartShowingInputView = false;
- if (needsToLoadKeyboard) {
- loadKeyboard();
- }
- }
-
- public void stopShowingInputView() {
- showWindow(false /* showInput */);
- }
-
- @Override
- public boolean onShowInputRequested(final int flags, final boolean configChange) {
- if (isImeSuppressedByHardwareKeyboard()) {
- return true;
- }
- return super.onShowInputRequested(flags, configChange);
- }
-
- @Override
- public boolean onEvaluateInputViewShown() {
- if (mIsExecutingStartShowingInputView) {
- return true;
- }
- return super.onEvaluateInputViewShown();
- }
-
- @Override
- public boolean onEvaluateFullscreenMode() {
- final SettingsValues settingsValues = mSettings.getCurrent();
- if (isImeSuppressedByHardwareKeyboard()) {
- // If there is a hardware keyboard, disable full screen mode.
- return false;
- }
- // Reread resource value here, because this method is called by the framework as needed.
- final boolean isFullscreenModeAllowed = Settings.readUseFullscreenMode(getResources());
- if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) {
- // TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI
- // implies NO_FULLSCREEN. However, the framework mistakenly does. i.e. NO_EXTRACT_UI
- // without NO_FULLSCREEN doesn't work as expected. Because of this we need this
- // hack for now. Let's get rid of this once the framework gets fixed.
- final EditorInfo ei = getCurrentInputEditorInfo();
- return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0));
- }
- return false;
- }
-
- @Override
- public void updateFullscreenMode() {
- super.updateFullscreenMode();
- updateSoftInputWindowLayoutParameters();
- }
-
- private void updateSoftInputWindowLayoutParameters() {
- // Override layout parameters to expand {@link SoftInputWindow} to the entire screen.
- // See {@link InputMethodService#setinputView(View)} and
- // {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}.
- final Window window = getWindow().getWindow();
- ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT);
- // This method may be called before {@link #setInputView(View)}.
- if (mInputView != null) {
- // In non-fullscreen mode, {@link InputView} and its parent inputArea should expand to
- // the entire screen and be placed at the bottom of {@link SoftInputWindow}.
- // In fullscreen mode, these shouldn't expand to the entire screen and should be
- // coexistent with {@link #mExtractedArea} above.
- // See {@link InputMethodService#setInputView(View) and
- // com.android.internal.R.layout.input_method.xml.
- final int layoutHeight = isFullscreenMode()
- ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
- final View inputArea = window.findViewById(android.R.id.inputArea);
- ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight);
- ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM);
- ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight);
- }
- }
-
- int getCurrentAutoCapsState() {
- return mInputLogic.getCurrentAutoCapsState(mSettings.getCurrent());
- }
-
- int getCurrentRecapitalizeState() {
- return mInputLogic.getCurrentRecapitalizeState();
- }
-
- /**
- * @param codePoints code points to get coordinates for.
- * @return x,y coordinates for this keyboard, as a flattened array.
- */
- public int[] getCoordinatesForCurrentKeyboard(final int[] codePoints) {
- final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
- if (null == keyboard) {
- return CoordinateUtils.newCoordinateArray(codePoints.length,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
- }
- return keyboard.getCoordinates(codePoints);
- }
-
- // Callback for the {@link SuggestionStripView}, to call when the important notice strip is
- // pressed.
- @Override
- public void showImportantNoticeContents() {
- PermissionsManager.get(this).requestPermissions(
- this /* PermissionsResultCallback */,
- null /* activity */, permission.READ_CONTACTS);
- }
-
- @Override
- public void onRequestPermissionsResult(boolean allGranted) {
- ImportantNoticeUtils.updateContactsNoticeShown(this /* context */);
- setNeutralSuggestionStrip();
- }
-
- public void displaySettingsDialog() {
- if (isShowingOptionDialog()) {
- return;
- }
- showSubtypeSelectorAndSettings();
- }
-
- @Override
- public boolean onCustomRequest(final int requestCode) {
- if (isShowingOptionDialog()) return false;
- switch (requestCode) {
- case Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER:
- if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
- mRichImm.getInputMethodManager().showInputMethodPicker();
- return true;
- }
- return false;
- }
- return false;
- }
-
- private boolean isShowingOptionDialog() {
- return mOptionsDialog != null && mOptionsDialog.isShowing();
- }
-
- public void switchLanguage(final InputMethodSubtype subtype) {
- final IBinder token = getWindow().getWindow().getAttributes().token;
- mRichImm.setInputMethodAndSubtype(token, subtype);
- }
-
- // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
- public void switchToNextSubtype() {
- final IBinder token = getWindow().getWindow().getAttributes().token;
- if (shouldSwitchToOtherInputMethods()) {
- mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
- return;
- }
- mSubtypeState.switchSubtype(token, mRichImm);
- }
-
- // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
- // alphabetic shift and shift while in symbol layout and get rid of this method.
- private int getCodePointForKeyboard(final int codePoint) {
- if (Constants.CODE_SHIFT == codePoint) {
- final Keyboard currentKeyboard = mKeyboardSwitcher.getKeyboard();
- if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
- return codePoint;
- }
- return Constants.CODE_SYMBOL_SHIFT;
- }
- return codePoint;
- }
-
- // Implementation of {@link KeyboardActionListener}.
- @Override
- public void onCodeInput(final int codePoint, final int x, final int y,
- final boolean isKeyRepeat) {
- // TODO: this processing does not belong inside LatinIME, the caller should be doing this.
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- // x and y include some padding, but everything down the line (especially native
- // code) needs the coordinates in the keyboard frame.
- // TODO: We should reconsider which coordinate system should be used to represent
- // keyboard event. Also we should pull this up -- LatinIME has no business doing
- // this transformation, it should be done already before calling onEvent.
- final int keyX = mainKeyboardView.getKeyX(x);
- final int keyY = mainKeyboardView.getKeyY(y);
- final Event event = createSoftwareKeypressEvent(getCodePointForKeyboard(codePoint),
- keyX, keyY, isKeyRepeat);
- onEvent(event);
- }
-
- // This method is public for testability of LatinIME, but also in the future it should
- // completely replace #onCodeInput.
- public void onEvent(@Nonnull final Event event) {
- if (Constants.CODE_SHORTCUT == event.mKeyCode) {
- mRichImm.switchToShortcutIme(this);
- }
- final InputTransaction completeInputTransaction =
- mInputLogic.onCodeInput(mSettings.getCurrent(), event,
- mKeyboardSwitcher.getKeyboardShiftMode(),
- mKeyboardSwitcher.getCurrentKeyboardScriptId(), mHandler);
- updateStateAfterInputTransaction(completeInputTransaction);
- mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
- }
-
- // A helper method to split the code point and the key code. Ultimately, they should not be
- // squashed into the same variable, and this method should be removed.
- // public for testing, as we don't want to copy the same logic into test code
- @Nonnull
- public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int keyX,
- final int keyY, final boolean isKeyRepeat) {
- final int keyCode;
- final int codePoint;
- if (keyCodeOrCodePoint <= 0) {
- keyCode = keyCodeOrCodePoint;
- codePoint = Event.NOT_A_CODE_POINT;
- } else {
- keyCode = Event.NOT_A_KEY_CODE;
- codePoint = keyCodeOrCodePoint;
- }
- return Event.createSoftwareKeypressEvent(codePoint, keyCode, keyX, keyY, isKeyRepeat);
- }
-
- // Called from PointerTracker through the KeyboardActionListener interface
- @Override
- public void onTextInput(final String rawText) {
- // TODO: have the keyboard pass the correct key code when we need it.
- final Event event = Event.createSoftwareTextEvent(rawText, Constants.CODE_OUTPUT_TEXT);
- final InputTransaction completeInputTransaction =
- mInputLogic.onTextInput(mSettings.getCurrent(), event,
- mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
- updateStateAfterInputTransaction(completeInputTransaction);
- mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
- }
-
- @Override
- public void onStartBatchInput() {
- mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
- mGestureConsumer.onGestureStarted(
- mRichImm.getCurrentSubtypeLocale(),
- mKeyboardSwitcher.getKeyboard());
- }
-
- @Override
- public void onUpdateBatchInput(final InputPointers batchPointers) {
- mInputLogic.onUpdateBatchInput(batchPointers);
- }
-
- @Override
- public void onEndBatchInput(final InputPointers batchPointers) {
- mInputLogic.onEndBatchInput(batchPointers);
- mGestureConsumer.onGestureCompleted(batchPointers);
- }
-
- @Override
- public void onCancelBatchInput() {
- mInputLogic.onCancelBatchInput(mHandler);
- mGestureConsumer.onGestureCanceled();
- }
-
- /**
- * To be called after the InputLogic has gotten a chance to act on the suggested words by the
- * IME for the full gesture, possibly updating the TextView to reflect the first suggestion.
- * <p>
- * This method must be run on the UI Thread.
- * @param suggestedWords suggested words by the IME for the full gesture.
- */
- public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) {
- mGestureConsumer.onImeSuggestionsProcessed(suggestedWords,
- mInputLogic.getComposingStart(), mInputLogic.getComposingLength(),
- mDictionaryFacilitator);
- }
-
- // This method must run on the UI Thread.
- void showGesturePreviewAndSuggestionStrip(@Nonnull final SuggestedWords suggestedWords,
- final boolean dismissGestureFloatingPreviewText) {
- showSuggestionStrip(suggestedWords);
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- mainKeyboardView.showGestureFloatingPreviewText(suggestedWords,
- dismissGestureFloatingPreviewText /* dismissDelayed */);
- }
-
- // Called from PointerTracker through the KeyboardActionListener interface
- @Override
- public void onFinishSlidingInput() {
- // User finished sliding input.
- mKeyboardSwitcher.onFinishSlidingInput(getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- }
-
- // Called from PointerTracker through the KeyboardActionListener interface
- @Override
- public void onCancelInput() {
- // User released a finger outside any key
- // Nothing to do so far.
- }
-
- public boolean hasSuggestionStripView() {
- return null != mSuggestionStripView;
- }
-
- private void setSuggestedWords(final SuggestedWords suggestedWords) {
- final SettingsValues currentSettingsValues = mSettings.getCurrent();
- mInputLogic.setSuggestedWords(suggestedWords);
- // TODO: Modify this when we support suggestions with hard keyboard
- if (!hasSuggestionStripView()) {
- return;
- }
- if (!onEvaluateInputViewShown()) {
- return;
- }
-
- final boolean shouldShowImportantNotice =
- ImportantNoticeUtils.shouldShowImportantNotice(this, currentSettingsValues);
- final boolean shouldShowSuggestionCandidates =
- currentSettingsValues.mInputAttributes.mShouldShowSuggestions
- && currentSettingsValues.isSuggestionsEnabledPerUserSettings();
- final boolean shouldShowSuggestionsStripUnlessPassword = shouldShowImportantNotice
- || currentSettingsValues.mShowsVoiceInputKey
- || shouldShowSuggestionCandidates
- || currentSettingsValues.isApplicationSpecifiedCompletionsOn();
- final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword
- && !currentSettingsValues.mInputAttributes.mIsPasswordField;
- mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, isFullscreenMode());
- if (!shouldShowSuggestionsStrip) {
- return;
- }
-
- final boolean isEmptyApplicationSpecifiedCompletions =
- currentSettingsValues.isApplicationSpecifiedCompletionsOn()
- && suggestedWords.isEmpty();
- final boolean noSuggestionsFromDictionaries = suggestedWords.isEmpty()
- || suggestedWords.isPunctuationSuggestions()
- || isEmptyApplicationSpecifiedCompletions;
- final boolean isBeginningOfSentencePrediction = (suggestedWords.mInputStyle
- == SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION);
- final boolean noSuggestionsToOverrideImportantNotice = noSuggestionsFromDictionaries
- || isBeginningOfSentencePrediction;
- if (shouldShowImportantNotice && noSuggestionsToOverrideImportantNotice) {
- if (mSuggestionStripView.maybeShowImportantNoticeTitle()) {
- return;
- }
- }
-
- if (currentSettingsValues.isSuggestionsEnabledPerUserSettings()
- || currentSettingsValues.isApplicationSpecifiedCompletionsOn()
- // We should clear the contextual strip if there is no suggestion from dictionaries.
- || noSuggestionsFromDictionaries) {
- mSuggestionStripView.setSuggestions(suggestedWords,
- mRichImm.getCurrentSubtype().isRtlSubtype());
- }
- }
-
- // TODO[IL]: Move this out of LatinIME.
- public void getSuggestedWords(final int inputStyle, final int sequenceNumber,
- final OnGetSuggestedWordsCallback callback) {
- final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
- if (keyboard == null) {
- callback.onGetSuggestedWords(SuggestedWords.getEmptyInstance());
- return;
- }
- mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard,
- mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback);
- }
-
- @Override
- public void showSuggestionStrip(final SuggestedWords suggestedWords) {
- if (suggestedWords.isEmpty()) {
- setNeutralSuggestionStrip();
- } else {
- setSuggestedWords(suggestedWords);
- }
- // Cache the auto-correction in accessibility code so we can speak it if the user
- // touches a key that will insert it.
- AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords);
- }
-
- // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
- // interface
- @Override
- public void pickSuggestionManually(final SuggestedWordInfo suggestionInfo) {
- final InputTransaction completeInputTransaction = mInputLogic.onPickSuggestionManually(
- mSettings.getCurrent(), suggestionInfo,
- mKeyboardSwitcher.getKeyboardShiftMode(),
- mKeyboardSwitcher.getCurrentKeyboardScriptId(),
- mHandler);
- updateStateAfterInputTransaction(completeInputTransaction);
- }
-
- // This will show either an empty suggestion strip (if prediction is enabled) or
- // punctuation suggestions (if it's disabled).
- @Override
- public void setNeutralSuggestionStrip() {
- final SettingsValues currentSettings = mSettings.getCurrent();
- final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled
- ? SuggestedWords.getEmptyInstance()
- : currentSettings.mSpacingAndPunctuations.mSuggestPuncList;
- setSuggestedWords(neutralSuggestions);
- }
-
- // Outside LatinIME, only used by the {@link InputTestsBase} test suite.
- @UsedForTesting
- void loadKeyboard() {
- // Since we are switching languages, the most urgent thing is to let the keyboard graphics
- // update. LoadKeyboard does that, but we need to wait for buffer flip for it to be on
- // the screen. Anything we do right now will delay this, so wait until the next frame
- // before we do the rest, like reopening dictionaries and updating suggestions. So we
- // post a message.
- mHandler.postReopenDictionaries();
- loadSettings();
- if (mKeyboardSwitcher.getMainKeyboardView() != null) {
- // Reload keyboard because the current language has been changed.
- mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent(),
- getCurrentAutoCapsState(), getCurrentRecapitalizeState());
- }
- }
-
- /**
- * After an input transaction has been executed, some state must be updated. This includes
- * the shift state of the keyboard and suggestions. This method looks at the finished
- * inputTransaction to find out what is necessary and updates the state accordingly.
- * @param inputTransaction The transaction that has been executed.
- */
- private void updateStateAfterInputTransaction(final InputTransaction inputTransaction) {
- switch (inputTransaction.getRequiredShiftUpdate()) {
- case InputTransaction.SHIFT_UPDATE_LATER:
- mHandler.postUpdateShiftState();
- break;
- case InputTransaction.SHIFT_UPDATE_NOW:
- mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- break;
- default: // SHIFT_NO_UPDATE
- }
- if (inputTransaction.requiresUpdateSuggestions()) {
- final int inputStyle;
- if (inputTransaction.mEvent.isSuggestionStripPress()) {
- // Suggestion strip press: no input.
- inputStyle = SuggestedWords.INPUT_STYLE_NONE;
- } else if (inputTransaction.mEvent.isGesture()) {
- inputStyle = SuggestedWords.INPUT_STYLE_TAIL_BATCH;
- } else {
- inputStyle = SuggestedWords.INPUT_STYLE_TYPING;
- }
- mHandler.postUpdateSuggestionStrip(inputStyle);
- }
- if (inputTransaction.didAffectContents()) {
- mSubtypeState.setCurrentSubtypeHasBeenUsed();
- }
- }
-
- private void hapticAndAudioFeedback(final int code, final int repeatCount) {
- final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (keyboardView != null && keyboardView.isInDraggingFinger()) {
- // No need to feedback while finger is dragging.
- return;
- }
- if (repeatCount > 0) {
- if (code == Constants.CODE_DELETE && !mInputLogic.mConnection.canDeleteCharacters()) {
- // No need to feedback when repeat delete key will have no effect.
- return;
- }
- // TODO: Use event time that the last feedback has been generated instead of relying on
- // a repeat count to thin out feedback.
- if (repeatCount % PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT == 0) {
- return;
- }
- }
- final AudioAndHapticFeedbackManager feedbackManager =
- AudioAndHapticFeedbackManager.getInstance();
- if (repeatCount == 0) {
- // TODO: Reconsider how to perform haptic feedback when repeating key.
- feedbackManager.performHapticFeedback(keyboardView);
- }
- feedbackManager.performAudioFeedback(code);
- }
-
- // Callback of the {@link KeyboardActionListener}. This is called when a key is depressed;
- // release matching call is {@link #onReleaseKey(int,boolean)} below.
- @Override
- public void onPressKey(final int primaryCode, final int repeatCount,
- final boolean isSinglePointer) {
- mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer, getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- hapticAndAudioFeedback(primaryCode, repeatCount);
- }
-
- // Callback of the {@link KeyboardActionListener}. This is called when a key is released;
- // press matching call is {@link #onPressKey(int,int,boolean)} above.
- @Override
- public void onReleaseKey(final int primaryCode, final boolean withSliding) {
- mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding, getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
- }
-
- private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) {
- final HardwareEventDecoder decoder = mHardwareEventDecoders.get(deviceId);
- if (null != decoder) return decoder;
- // TODO: create the decoder according to the specification
- final HardwareEventDecoder newDecoder = new HardwareKeyboardEventDecoder(deviceId);
- mHardwareEventDecoders.put(deviceId, newDecoder);
- return newDecoder;
- }
-
- // Hooks for hardware keyboard
- @Override
- public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
- if (mEmojiAltPhysicalKeyDetector == null) {
- mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector(
- getApplicationContext().getResources());
- }
- mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent);
- if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
- return super.onKeyDown(keyCode, keyEvent);
- }
- final Event event = getHardwareKeyEventDecoder(
- keyEvent.getDeviceId()).decodeHardwareKey(keyEvent);
- // If the event is not handled by LatinIME, we just pass it to the parent implementation.
- // If it's handled, we return true because we did handle it.
- if (event.isHandled()) {
- mInputLogic.onCodeInput(mSettings.getCurrent(), event,
- mKeyboardSwitcher.getKeyboardShiftMode(),
- // TODO: this is not necessarily correct for a hardware keyboard right now
- mKeyboardSwitcher.getCurrentKeyboardScriptId(),
- mHandler);
- return true;
- }
- return super.onKeyDown(keyCode, keyEvent);
- }
-
- @Override
- public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
- if (mEmojiAltPhysicalKeyDetector == null) {
- mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector(
- getApplicationContext().getResources());
- }
- mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent);
- if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
- return super.onKeyUp(keyCode, keyEvent);
- }
- final long keyIdentifier = keyEvent.getDeviceId() << 32 + keyEvent.getKeyCode();
- if (mInputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier)) {
- return true;
- }
- return super.onKeyUp(keyCode, keyEvent);
- }
-
- // onKeyDown and onKeyUp are the main events we are interested in. There are two more events
- // related to handling of hardware key events that we may want to implement in the future:
- // boolean onKeyLongPress(final int keyCode, final KeyEvent event);
- // boolean onKeyMultiple(final int keyCode, final int count, final KeyEvent event);
-
- // receive ringer mode change.
- private final BroadcastReceiver mRingerModeChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- final String action = intent.getAction();
- if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- AudioAndHapticFeedbackManager.getInstance().onRingerModeChanged();
- }
- }
- };
-
- /**
- * Starts {@link android.app.Activity} on the same display where the IME is shown.
- *
- * @param intent {@link Intent} to be used to start {@link android.app.Activity}.
- */
- private void startActivityOnTheSameDisplay(Intent intent) {
- // Note that WindowManager#getDefaultDisplay() returns the display ID associated with the
- // Context from which the WindowManager instance was obtained. Therefore the following code
- // returns the display ID for the window where the IME is shown.
- final int currentDisplayId = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay().getDisplayId();
-
- startActivity(intent,
- ActivityOptions.makeBasic().setLaunchDisplayId(currentDisplayId).toBundle());
- }
-
- void launchSettings(final String extraEntryValue) {
- mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
- requestHideSelf(0);
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (mainKeyboardView != null) {
- mainKeyboardView.closing();
- }
- final Intent intent = new Intent();
- intent.setClass(LatinIME.this, SettingsActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(SettingsActivity.EXTRA_SHOW_HOME_AS_UP, false);
- intent.putExtra(SettingsActivity.EXTRA_ENTRY_KEY, extraEntryValue);
- startActivityOnTheSameDisplay(intent);
- }
-
- private void showSubtypeSelectorAndSettings() {
- final CharSequence title = getString(R.string.english_ime_input_options);
- // TODO: Should use new string "Select active input modes".
- final CharSequence languageSelectionTitle = getString(R.string.language_selection_title);
- final CharSequence[] items = new CharSequence[] {
- languageSelectionTitle,
- getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class))
- };
- final String imeId = mRichImm.getInputMethodIdOfThisIme();
- final OnClickListener listener = new OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int position) {
- di.dismiss();
- switch (position) {
- case 0:
- final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
- imeId,
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(Intent.EXTRA_TITLE, languageSelectionTitle);
- startActivityOnTheSameDisplay(intent);
- break;
- case 1:
- launchSettings(SettingsActivity.EXTRA_ENTRY_VALUE_LONG_PRESS_COMMA);
- break;
- }
- }
- };
- final AlertDialog.Builder builder = new AlertDialog.Builder(
- DialogUtils.getPlatformDialogThemeContext(this));
- builder.setItems(items, listener).setTitle(title);
- final AlertDialog dialog = builder.create();
- dialog.setCancelable(true /* cancelable */);
- dialog.setCanceledOnTouchOutside(true /* cancelable */);
- showOptionDialog(dialog);
- }
-
- // TODO: Move this method out of {@link LatinIME}.
- private void showOptionDialog(final AlertDialog dialog) {
- final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
- if (windowToken == null) {
- return;
- }
-
- final Window window = dialog.getWindow();
- final WindowManager.LayoutParams lp = window.getAttributes();
- lp.token = windowToken;
- lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
- window.setAttributes(lp);
- window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
- mOptionsDialog = dialog;
- dialog.show();
- }
-
- @UsedForTesting
- SuggestedWords getSuggestedWordsForTest() {
- // You may not use this method for anything else than debug
- return DebugFlags.DEBUG_ENABLED ? mInputLogic.mSuggestedWords : null;
- }
-
- // DO NOT USE THIS for any other purpose than testing. This is information private to LatinIME.
- @UsedForTesting
- void waitForLoadingDictionaries(final long timeout, final TimeUnit unit)
- throws InterruptedException {
- mDictionaryFacilitator.waitForLoadingDictionariesForTesting(timeout, unit);
- }
-
- // DO NOT USE THIS for any other purpose than testing. This can break the keyboard badly.
- @UsedForTesting
- void replaceDictionariesForTest(final Locale locale) {
- final SettingsValues settingsValues = mSettings.getCurrent();
- mDictionaryFacilitator.resetDictionaries(this, locale,
- settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
- false /* forceReloadMainDictionary */,
- settingsValues.mAccount, "", /* dictionaryNamePrefix */
- this /* DictionaryInitializationListener */);
- }
-
- // DO NOT USE THIS for any other purpose than testing.
- @UsedForTesting
- void clearPersonalizedDictionariesForTest() {
- mDictionaryFacilitator.clearUserHistoryDictionary(this);
- }
-
- @UsedForTesting
- List<InputMethodSubtype> getEnabledSubtypesForTest() {
- return (mRichImm != null) ? mRichImm.getMyEnabledInputMethodSubtypeList(
- true /* allowsImplicitlySelectedSubtypes */) : new ArrayList<InputMethodSubtype>();
- }
-
- public void dumpDictionaryForDebug(final String dictName) {
- if (!mDictionaryFacilitator.isActive()) {
- resetDictionaryFacilitatorIfNecessary();
- }
- mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
- }
-
- public void debugDumpStateAndCrashWithException(final String context) {
- final SettingsValues settingsValues = mSettings.getCurrent();
- final StringBuilder s = new StringBuilder(settingsValues.toString());
- s.append("\nAttributes : ").append(settingsValues.mInputAttributes)
- .append("\nContext : ").append(context);
- throw new RuntimeException(s.toString());
- }
-
- @Override
- protected void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
- super.dump(fd, fout, args);
-
- final Printer p = new PrintWriterPrinter(fout);
- p.println("LatinIME state :");
- p.println(" VersionCode = " + ApplicationUtils.getVersionCode(this));
- p.println(" VersionName = " + ApplicationUtils.getVersionName(this));
- final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
- final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
- p.println(" Keyboard mode = " + keyboardMode);
- final SettingsValues settingsValues = mSettings.getCurrent();
- p.println(settingsValues.dump());
- p.println(mDictionaryFacilitator.dump(this /* context */));
- // TODO: Dump all settings values
- }
-
- public boolean shouldSwitchToOtherInputMethods() {
- // TODO: Revisit here to reorganize the settings. Probably we can/should use different
- // strategy once the implementation of
- // {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod} is defined well.
- final boolean fallbackValue = mSettings.getCurrent().mIncludesOtherImesInLanguageSwitchList;
- final IBinder token = getWindow().getWindow().getAttributes().token;
- if (token == null) {
- return fallbackValue;
- }
- return mRichImm.shouldOfferSwitchingToNextInputMethod(token, fallbackValue);
- }
-
- public boolean shouldShowLanguageSwitchKey() {
- // TODO: Revisit here to reorganize the settings. Probably we can/should use different
- // strategy once the implementation of
- // {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod} is defined well.
- final boolean fallbackValue = mSettings.getCurrent().isLanguageSwitchKeyEnabled();
- final IBinder token = getWindow().getWindow().getAttributes().token;
- if (token == null) {
- return fallbackValue;
- }
- return mRichImm.shouldOfferSwitchingToNextInputMethod(token, fallbackValue);
- }
-
- private void setNavigationBarVisibility(final boolean visible) {
- if (BuildCompatUtils.EFFECTIVE_SDK_INT > Build.VERSION_CODES.M) {
- // For N and later, IMEs can specify Color.TRANSPARENT to make the navigation bar
- // transparent. For other colors the system uses the default color.
- getWindow().getWindow().setNavigationBarColor(
- visible ? Color.BLACK : Color.TRANSPARENT);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java
deleted file mode 100644
index 9682fb8a4..000000000
--- a/java/src/com/android/inputmethod/latin/NgramContext.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.text.TextUtils;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import javax.annotation.Nonnull;
-
-/**
- * Class to represent information of previous words. This class is used to add n-gram entries
- * into binary dictionaries, to get predictions, and to get suggestions.
- */
-public class NgramContext {
- @Nonnull
- public static final NgramContext EMPTY_PREV_WORDS_INFO =
- new NgramContext(WordInfo.EMPTY_WORD_INFO);
- @Nonnull
- public static final NgramContext BEGINNING_OF_SENTENCE =
- new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO);
-
- public static final String BEGINNING_OF_SENTENCE_TAG = "<S>";
-
- public static final String CONTEXT_SEPARATOR = " ";
-
- public static NgramContext getEmptyPrevWordsContext(int maxPrevWordCount) {
- return new NgramContext(maxPrevWordCount, WordInfo.EMPTY_WORD_INFO);
- }
-
- /**
- * Word information used to represent previous words information.
- */
- public static class WordInfo {
- @Nonnull
- public static final WordInfo EMPTY_WORD_INFO = new WordInfo(null);
- @Nonnull
- public static final WordInfo BEGINNING_OF_SENTENCE_WORD_INFO = new WordInfo();
-
- // This is an empty char sequence when mIsBeginningOfSentence is true.
- public final CharSequence mWord;
- // TODO: Have sentence separator.
- // Whether the current context is beginning of sentence or not. This is true when composing
- // at the beginning of an input field or composing a word after a sentence separator.
- public final boolean mIsBeginningOfSentence;
-
- // Beginning of sentence.
- private WordInfo() {
- mWord = "";
- mIsBeginningOfSentence = true;
- }
-
- public WordInfo(final CharSequence word) {
- mWord = word;
- mIsBeginningOfSentence = false;
- }
-
- public boolean isValid() {
- return mWord != null;
- }
-
- @Override
- public int hashCode() {
- return Arrays.hashCode(new Object[] { mWord, mIsBeginningOfSentence } );
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof WordInfo)) return false;
- final WordInfo wordInfo = (WordInfo)o;
- if (mWord == null || wordInfo.mWord == null) {
- return mWord == wordInfo.mWord
- && mIsBeginningOfSentence == wordInfo.mIsBeginningOfSentence;
- }
- return TextUtils.equals(mWord, wordInfo.mWord)
- && mIsBeginningOfSentence == wordInfo.mIsBeginningOfSentence;
- }
- }
-
- // The words immediately before the considered word. EMPTY_WORD_INFO element means we don't
- // have any context for that previous word including the "beginning of sentence context" - we
- // just don't know what to predict using the information. An example of that is after a comma.
- // For simplicity of implementation, elements may also be EMPTY_WORD_INFO transiently after the
- // WordComposer was reset and before starting a new composing word, but we should never be
- // calling getSuggetions* in this situation.
- private final WordInfo[] mPrevWordsInfo;
- private final int mPrevWordsCount;
-
- private final int mMaxPrevWordCount;
-
- // Construct from the previous word information.
- public NgramContext(final WordInfo... prevWordsInfo) {
- this(DecoderSpecificConstants.MAX_PREV_WORD_COUNT_FOR_N_GRAM, prevWordsInfo);
- }
-
- public NgramContext(final int maxPrevWordCount, final WordInfo... prevWordsInfo) {
- mPrevWordsInfo = prevWordsInfo;
- mPrevWordsCount = prevWordsInfo.length;
- mMaxPrevWordCount = maxPrevWordCount;
- }
-
- /**
- * Create next prevWordsInfo using current prevWordsInfo.
- */
- @Nonnull
- public NgramContext getNextNgramContext(final WordInfo wordInfo) {
- final int nextPrevWordCount = Math.min(mMaxPrevWordCount, mPrevWordsCount + 1);
- final WordInfo[] prevWordsInfo = new WordInfo[nextPrevWordCount];
- prevWordsInfo[0] = wordInfo;
- System.arraycopy(mPrevWordsInfo, 0, prevWordsInfo, 1, nextPrevWordCount - 1);
- return new NgramContext(mMaxPrevWordCount, prevWordsInfo);
- }
-
-
- /**
- * Extracts the previous words context.
- *
- * @return a String with the previous words separated by white space.
- */
- public String extractPrevWordsContext() {
- final ArrayList<String> terms = new ArrayList<>();
- for (int i = mPrevWordsInfo.length - 1; i >= 0; --i) {
- if (mPrevWordsInfo[i] != null && mPrevWordsInfo[i].isValid()) {
- final NgramContext.WordInfo wordInfo = mPrevWordsInfo[i];
- if (wordInfo.mIsBeginningOfSentence) {
- terms.add(BEGINNING_OF_SENTENCE_TAG);
- } else {
- final String term = wordInfo.mWord.toString();
- if (!term.isEmpty()) {
- terms.add(term);
- }
- }
- }
- }
- return TextUtils.join(CONTEXT_SEPARATOR, terms);
- }
-
- /**
- * Extracts the previous words context.
- *
- * @return a String array with the previous words.
- */
- public String[] extractPrevWordsContextArray() {
- final ArrayList<String> prevTermList = new ArrayList<>();
- for (int i = mPrevWordsInfo.length - 1; i >= 0; --i) {
- if (mPrevWordsInfo[i] != null && mPrevWordsInfo[i].isValid()) {
- final NgramContext.WordInfo wordInfo = mPrevWordsInfo[i];
- if (wordInfo.mIsBeginningOfSentence) {
- prevTermList.add(BEGINNING_OF_SENTENCE_TAG);
- } else {
- final String term = wordInfo.mWord.toString();
- if (!term.isEmpty()) {
- prevTermList.add(term);
- }
- }
- }
- }
- final String[] contextStringArray = prevTermList.toArray(new String[prevTermList.size()]);
- return contextStringArray;
- }
-
- public boolean isValid() {
- return mPrevWordsCount > 0 && mPrevWordsInfo[0].isValid();
- }
-
- public boolean isBeginningOfSentenceContext() {
- return mPrevWordsCount > 0 && mPrevWordsInfo[0].mIsBeginningOfSentence;
- }
-
- // n is 1-indexed.
- // TODO: Remove
- public CharSequence getNthPrevWord(final int n) {
- if (n <= 0 || n > mPrevWordsCount) {
- return null;
- }
- return mPrevWordsInfo[n - 1].mWord;
- }
-
- // n is 1-indexed.
- @UsedForTesting
- public boolean isNthPrevWordBeginningOfSentence(final int n) {
- if (n <= 0 || n > mPrevWordsCount) {
- return false;
- }
- return mPrevWordsInfo[n - 1].mIsBeginningOfSentence;
- }
-
- public void outputToArray(final int[][] codePointArrays,
- final boolean[] isBeginningOfSentenceArray) {
- for (int i = 0; i < mPrevWordsCount; i++) {
- final WordInfo wordInfo = mPrevWordsInfo[i];
- if (wordInfo == null || !wordInfo.isValid()) {
- codePointArrays[i] = new int[0];
- isBeginningOfSentenceArray[i] = false;
- continue;
- }
- codePointArrays[i] = StringUtils.toCodePointArray(wordInfo.mWord);
- isBeginningOfSentenceArray[i] = wordInfo.mIsBeginningOfSentence;
- }
- }
-
- public int getPrevWordCount() {
- return mPrevWordsCount;
- }
-
- @Override
- public int hashCode() {
- int hashValue = 0;
- for (final WordInfo wordInfo : mPrevWordsInfo) {
- if (wordInfo == null || !WordInfo.EMPTY_WORD_INFO.equals(wordInfo)) {
- break;
- }
- hashValue ^= wordInfo.hashCode();
- }
- return hashValue;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof NgramContext)) return false;
- final NgramContext prevWordsInfo = (NgramContext)o;
-
- final int minLength = Math.min(mPrevWordsCount, prevWordsInfo.mPrevWordsCount);
- for (int i = 0; i < minLength; i++) {
- if (!mPrevWordsInfo[i].equals(prevWordsInfo.mPrevWordsInfo[i])) {
- return false;
- }
- }
- final WordInfo[] longerWordsInfo;
- final int longerWordsInfoCount;
- if (mPrevWordsCount > prevWordsInfo.mPrevWordsCount) {
- longerWordsInfo = mPrevWordsInfo;
- longerWordsInfoCount = mPrevWordsCount;
- } else {
- longerWordsInfo = prevWordsInfo.mPrevWordsInfo;
- longerWordsInfoCount = prevWordsInfo.mPrevWordsCount;
- }
- for (int i = minLength; i < longerWordsInfoCount; i++) {
- if (longerWordsInfo[i] != null
- && !WordInfo.EMPTY_WORD_INFO.equals(longerWordsInfo[i])) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- final StringBuffer builder = new StringBuffer();
- for (int i = 0; i < mPrevWordsCount; i++) {
- final WordInfo wordInfo = mPrevWordsInfo[i];
- builder.append("PrevWord[");
- builder.append(i);
- builder.append("]: ");
- if (wordInfo == null) {
- builder.append("null. ");
- continue;
- }
- if (!wordInfo.isValid()) {
- builder.append("Empty. ");
- continue;
- }
- builder.append(wordInfo.mWord);
- builder.append(", isBeginningOfSentence: ");
- builder.append(wordInfo.mIsBeginningOfSentence);
- builder.append(". ");
- }
- return builder.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
deleted file mode 100644
index e2c562174..000000000
--- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.keyboard.internal.KeySpecParser;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import javax.annotation.Nullable;
-
-/**
- * The extended {@link SuggestedWords} class to represent punctuation suggestions.
- *
- * Each punctuation specification string is the key specification that can be parsed by
- * {@link KeySpecParser}.
- */
-public final class PunctuationSuggestions extends SuggestedWords {
- private PunctuationSuggestions(final ArrayList<SuggestedWordInfo> punctuationsList) {
- super(punctuationsList,
- null /* rawSuggestions */,
- null /* typedWord */,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- false /* isObsoleteSuggestions */,
- INPUT_STYLE_NONE /* inputStyle */,
- SuggestedWords.NOT_A_SEQUENCE_NUMBER);
- }
-
- /**
- * Create new instance of {@link PunctuationSuggestions} from the array of punctuation key
- * specifications.
- *
- * @param punctuationSpecs The array of punctuation key specifications.
- * @return The {@link PunctuationSuggestions} object.
- */
- public static PunctuationSuggestions newPunctuationSuggestions(
- @Nullable final String[] punctuationSpecs) {
- if (punctuationSpecs == null || punctuationSpecs.length == 0) {
- return new PunctuationSuggestions(new ArrayList<SuggestedWordInfo>(0));
- }
- final ArrayList<SuggestedWordInfo> punctuationList =
- new ArrayList<>(punctuationSpecs.length);
- for (String spec : punctuationSpecs) {
- punctuationList.add(newHardCodedWordInfo(spec));
- }
- return new PunctuationSuggestions(punctuationList);
- }
-
- /**
- * {@inheritDoc}
- * Note that {@link SuggestedWords#getWord(int)} returns a punctuation key specification text.
- * The suggested punctuation should be gotten by parsing the key specification.
- */
- @Override
- public String getWord(final int index) {
- final String keySpec = super.getWord(index);
- final int code = KeySpecParser.getCode(keySpec);
- return (code == Constants.CODE_OUTPUT_TEXT)
- ? KeySpecParser.getOutputText(keySpec)
- : StringUtils.newSingleCodePointString(code);
- }
-
- /**
- * {@inheritDoc}
- * Note that {@link SuggestedWords#getWord(int)} returns a punctuation key specification text.
- * The displayed text should be gotten by parsing the key specification.
- */
- @Override
- public String getLabel(final int index) {
- final String keySpec = super.getWord(index);
- return KeySpecParser.getLabel(keySpec);
- }
-
- /**
- * {@inheritDoc}
- * Note that {@link #getWord(int)} returns a suggested punctuation. We should create a
- * {@link SuggestedWords.SuggestedWordInfo} object that represents a hard coded word.
- */
- @Override
- public SuggestedWordInfo getInfo(final int index) {
- return newHardCodedWordInfo(getWord(index));
- }
-
- /**
- * The predicator to tell whether this object represents punctuation suggestions.
- * @return true if this object represents punctuation suggestions.
- */
- @Override
- public boolean isPunctuationSuggestions() {
- return true;
- }
-
- @Override
- public String toString() {
- return "PunctuationSuggestions: "
- + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
- }
-
- private static SuggestedWordInfo newHardCodedWordInfo(final String keySpec) {
- return new SuggestedWordInfo(keySpec, "" /* prevWordsContext */,
- SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_HARDCODED,
- Dictionary.DICTIONARY_HARDCODED,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
deleted file mode 100644
index 7b1a53a6e..000000000
--- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * This class provides binary dictionary reading operations with locking. An instance of this class
- * can be used by multiple threads. Note that different session IDs must be used when multiple
- * threads get suggestions using this class.
- */
-public final class ReadOnlyBinaryDictionary extends Dictionary {
- /**
- * A lock for accessing binary dictionary. Only closing binary dictionary is the operation
- * that change the state of dictionary.
- */
- private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
-
- private final BinaryDictionary mBinaryDictionary;
-
- public ReadOnlyBinaryDictionary(final String filename, final long offset, final long length,
- final boolean useFullEditDistance, final Locale locale, final String dictType) {
- super(dictType, locale);
- mBinaryDictionary = new BinaryDictionary(filename, offset, length, useFullEditDistance,
- locale, dictType, false /* isUpdatable */);
- }
-
- public boolean isValidDictionary() {
- return mBinaryDictionary.isValidDictionary();
- }
-
- @Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
- final NgramContext ngramContext, final long proximityInfoHandle,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int sessionId, final float weightForLocale,
- final float[] inOutWeightOfLangModelVsSpatialModel) {
- if (mLock.readLock().tryLock()) {
- try {
- return mBinaryDictionary.getSuggestions(composedData, ngramContext,
- proximityInfoHandle, settingsValuesForSuggestion, sessionId,
- weightForLocale, inOutWeightOfLangModelVsSpatialModel);
- } finally {
- mLock.readLock().unlock();
- }
- }
- return null;
- }
-
- @Override
- public boolean isInDictionary(final String word) {
- if (mLock.readLock().tryLock()) {
- try {
- return mBinaryDictionary.isInDictionary(word);
- } finally {
- mLock.readLock().unlock();
- }
- }
- return false;
- }
-
- @Override
- public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
- if (mLock.readLock().tryLock()) {
- try {
- return mBinaryDictionary.shouldAutoCommit(candidate);
- } finally {
- mLock.readLock().unlock();
- }
- }
- return false;
- }
-
- @Override
- public int getFrequency(final String word) {
- if (mLock.readLock().tryLock()) {
- try {
- return mBinaryDictionary.getFrequency(word);
- } finally {
- mLock.readLock().unlock();
- }
- }
- return NOT_A_PROBABILITY;
- }
-
- @Override
- public int getMaxFrequencyOfExactMatches(final String word) {
- if (mLock.readLock().tryLock()) {
- try {
- return mBinaryDictionary.getMaxFrequencyOfExactMatches(word);
- } finally {
- mLock.readLock().unlock();
- }
- }
- return NOT_A_PROBABILITY;
- }
-
- @Override
- public void close() {
- mLock.writeLock().lock();
- try {
- mBinaryDictionary.close();
- } finally {
- mLock.writeLock().unlock();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
deleted file mode 100644
index a10f2bdb0..000000000
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ /dev/null
@@ -1,1033 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.style.CharacterStyle;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.inputmethod.compat.InputConnectionCompatUtils;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.UnicodeSurrogate;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-import com.android.inputmethod.latin.utils.CapsModeUtils;
-import com.android.inputmethod.latin.utils.DebugLogUtils;
-import com.android.inputmethod.latin.utils.NgramContextUtils;
-import com.android.inputmethod.latin.utils.ScriptUtils;
-import com.android.inputmethod.latin.utils.SpannableStringUtils;
-import com.android.inputmethod.latin.utils.StatsUtils;
-import com.android.inputmethod.latin.utils.TextRange;
-
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Enrichment class for InputConnection to simplify interaction and add functionality.
- *
- * This class serves as a wrapper to be able to simply add hooks to any calls to the underlying
- * InputConnection. It also keeps track of a number of things to avoid having to call upon IPC
- * all the time to find out what text is in the buffer, when we need it to determine caps mode
- * for example.
- */
-public final class RichInputConnection implements PrivateCommandPerformer {
- private static final String TAG = "RichInputConnection";
- private static final boolean DBG = false;
- private static final boolean DEBUG_PREVIOUS_TEXT = false;
- private static final boolean DEBUG_BATCH_NESTING = false;
- private static final int NUM_CHARS_TO_GET_BEFORE_CURSOR = 40;
- private static final int NUM_CHARS_TO_GET_AFTER_CURSOR = 40;
- private static final int INVALID_CURSOR_POSITION = -1;
-
- /**
- * The amount of time a {@link #reloadTextCache} call needs to take for the keyboard to enter
- * the {@link #hasSlowInputConnection} state.
- */
- private static final long SLOW_INPUT_CONNECTION_ON_FULL_RELOAD_MS = 1000;
- /**
- * The amount of time a {@link #getTextBeforeCursor} or {@link #getTextAfterCursor} call needs
- * to take for the keyboard to enter the {@link #hasSlowInputConnection} state.
- */
- private static final long SLOW_INPUT_CONNECTION_ON_PARTIAL_RELOAD_MS = 200;
-
- private static final int OPERATION_GET_TEXT_BEFORE_CURSOR = 0;
- private static final int OPERATION_GET_TEXT_AFTER_CURSOR = 1;
- private static final int OPERATION_GET_WORD_RANGE_AT_CURSOR = 2;
- private static final int OPERATION_RELOAD_TEXT_CACHE = 3;
- private static final String[] OPERATION_NAMES = new String[] {
- "GET_TEXT_BEFORE_CURSOR",
- "GET_TEXT_AFTER_CURSOR",
- "GET_WORD_RANGE_AT_CURSOR",
- "RELOAD_TEXT_CACHE"};
-
- /**
- * The amount of time the keyboard will persist in the {@link #hasSlowInputConnection} state
- * after observing a slow InputConnection event.
- */
- private static final long SLOW_INPUTCONNECTION_PERSIST_MS = TimeUnit.MINUTES.toMillis(10);
-
- /**
- * This variable contains an expected value for the selection start position. This is where the
- * cursor or selection start may end up after all the keyboard-triggered updates have passed. We
- * keep this to compare it to the actual selection start to guess whether the move was caused by
- * a keyboard command or not.
- * It's not really the selection start position: the selection start may not be there yet, and
- * in some cases, it may never arrive there.
- */
- private int mExpectedSelStart = INVALID_CURSOR_POSITION; // in chars, not code points
- /**
- * The expected selection end. Only differs from mExpectedSelStart if a non-empty selection is
- * expected. The same caveats as mExpectedSelStart apply.
- */
- private int mExpectedSelEnd = INVALID_CURSOR_POSITION; // in chars, not code points
- /**
- * This contains the committed text immediately preceding the cursor and the composing
- * text, if any. It is refreshed when the cursor moves by calling upon the TextView.
- */
- private final StringBuilder mCommittedTextBeforeComposingText = new StringBuilder();
- /**
- * This contains the currently composing text, as LatinIME thinks the TextView is seeing it.
- */
- private final StringBuilder mComposingText = new StringBuilder();
-
- /**
- * This variable is a temporary object used in {@link #commitText(CharSequence,int)}
- * to avoid object creation.
- */
- private SpannableStringBuilder mTempObjectForCommitText = new SpannableStringBuilder();
-
- private final InputMethodService mParent;
- private InputConnection mIC;
- private int mNestLevel;
-
- /**
- * The timestamp of the last slow InputConnection operation
- */
- private long mLastSlowInputConnectionTime = -SLOW_INPUTCONNECTION_PERSIST_MS;
-
- public RichInputConnection(final InputMethodService parent) {
- mParent = parent;
- mIC = null;
- mNestLevel = 0;
- }
-
- public boolean isConnected() {
- return mIC != null;
- }
-
- /**
- * Returns whether or not the underlying InputConnection is slow. When true, we want to avoid
- * calling InputConnection methods that trigger an IPC round-trip (e.g., getTextAfterCursor).
- */
- public boolean hasSlowInputConnection() {
- return (SystemClock.uptimeMillis() - mLastSlowInputConnectionTime)
- <= SLOW_INPUTCONNECTION_PERSIST_MS;
- }
-
- public void onStartInput() {
- mLastSlowInputConnectionTime = -SLOW_INPUTCONNECTION_PERSIST_MS;
- }
-
- private void checkConsistencyForDebug() {
- final ExtractedTextRequest r = new ExtractedTextRequest();
- r.hintMaxChars = 0;
- r.hintMaxLines = 0;
- r.token = 1;
- r.flags = 0;
- final ExtractedText et = mIC.getExtractedText(r, 0);
- final CharSequence beforeCursor = getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE,
- 0);
- final StringBuilder internal = new StringBuilder(mCommittedTextBeforeComposingText)
- .append(mComposingText);
- if (null == et || null == beforeCursor) return;
- final int actualLength = Math.min(beforeCursor.length(), internal.length());
- if (internal.length() > actualLength) {
- internal.delete(0, internal.length() - actualLength);
- }
- final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString()
- : beforeCursor.subSequence(beforeCursor.length() - actualLength,
- beforeCursor.length()).toString();
- if (et.selectionStart != mExpectedSelStart
- || !(reference.equals(internal.toString()))) {
- final String context = "Expected selection start = " + mExpectedSelStart
- + "\nActual selection start = " + et.selectionStart
- + "\nExpected text = " + internal.length() + " " + internal
- + "\nActual text = " + reference.length() + " " + reference;
- ((LatinIME)mParent).debugDumpStateAndCrashWithException(context);
- } else {
- Log.e(TAG, DebugLogUtils.getStackTrace(2));
- Log.e(TAG, "Exp <> Actual : " + mExpectedSelStart + " <> " + et.selectionStart);
- }
- }
-
- public void beginBatchEdit() {
- if (++mNestLevel == 1) {
- mIC = mParent.getCurrentInputConnection();
- if (isConnected()) {
- mIC.beginBatchEdit();
- }
- } else {
- if (DBG) {
- throw new RuntimeException("Nest level too deep");
- }
- Log.e(TAG, "Nest level too deep : " + mNestLevel);
- }
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- }
-
- public void endBatchEdit() {
- if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead
- if (--mNestLevel == 0 && isConnected()) {
- mIC.endBatchEdit();
- }
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- }
-
- /**
- * Reset the cached text and retrieve it again from the editor.
- *
- * This should be called when the cursor moved. It's possible that we can't connect to
- * the application when doing this; notably, this happens sometimes during rotation, probably
- * because of a race condition in the framework. In this case, we just can't retrieve the
- * data, so we empty the cache and note that we don't know the new cursor position, and we
- * return false so that the caller knows about this and can retry later.
- *
- * @param newSelStart the new position of the selection start, as received from the system.
- * @param newSelEnd the new position of the selection end, as received from the system.
- * @param shouldFinishComposition whether we should finish the composition in progress.
- * @return true if we were able to connect to the editor successfully, false otherwise. When
- * this method returns false, the caches could not be correctly refreshed so they were only
- * reset: the caller should try again later to return to normal operation.
- */
- public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart,
- final int newSelEnd, final boolean shouldFinishComposition) {
- mExpectedSelStart = newSelStart;
- mExpectedSelEnd = newSelEnd;
- mComposingText.setLength(0);
- final boolean didReloadTextSuccessfully = reloadTextCache();
- if (!didReloadTextSuccessfully) {
- Log.d(TAG, "Will try to retrieve text later.");
- return false;
- }
- if (isConnected() && shouldFinishComposition) {
- mIC.finishComposingText();
- }
- return true;
- }
-
- /**
- * Reload the cached text from the InputConnection.
- *
- * @return true if successful
- */
- private boolean reloadTextCache() {
- mCommittedTextBeforeComposingText.setLength(0);
- mIC = mParent.getCurrentInputConnection();
- // Call upon the inputconnection directly since our own method is using the cache, and
- // we want to refresh it.
- final CharSequence textBeforeCursor = getTextBeforeCursorAndDetectLaggyConnection(
- OPERATION_RELOAD_TEXT_CACHE,
- SLOW_INPUT_CONNECTION_ON_FULL_RELOAD_MS,
- Constants.EDITOR_CONTENTS_CACHE_SIZE,
- 0 /* flags */);
- if (null == textBeforeCursor) {
- // For some reason the app thinks we are not connected to it. This looks like a
- // framework bug... Fall back to ground state and return false.
- mExpectedSelStart = INVALID_CURSOR_POSITION;
- mExpectedSelEnd = INVALID_CURSOR_POSITION;
- Log.e(TAG, "Unable to connect to the editor to retrieve text.");
- return false;
- }
- mCommittedTextBeforeComposingText.append(textBeforeCursor);
- return true;
- }
-
- private void checkBatchEdit() {
- if (mNestLevel != 1) {
- // TODO: exception instead
- Log.e(TAG, "Batch edit level incorrect : " + mNestLevel);
- Log.e(TAG, DebugLogUtils.getStackTrace(4));
- }
- }
-
- public void finishComposingText() {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- // TODO: this is not correct! The cursor is not necessarily after the composing text.
- // In the practice right now this is only called when input ends so it will be reset so
- // it works, but it's wrong and should be fixed.
- mCommittedTextBeforeComposingText.append(mComposingText);
- mComposingText.setLength(0);
- if (isConnected()) {
- mIC.finishComposingText();
- }
- }
-
- /**
- * Calls {@link InputConnection#commitText(CharSequence, int)}.
- *
- * @param text The text to commit. This may include styles.
- * @param newCursorPosition The new cursor position around the text.
- */
- public void commitText(final CharSequence text, final int newCursorPosition) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- mCommittedTextBeforeComposingText.append(text);
- // TODO: the following is exceedingly error-prone. Right now when the cursor is in the
- // middle of the composing word mComposingText only holds the part of the composing text
- // that is before the cursor, so this actually works, but it's terribly confusing. Fix this.
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
- mComposingText.setLength(0);
- if (isConnected()) {
- mTempObjectForCommitText.clear();
- mTempObjectForCommitText.append(text);
- final CharacterStyle[] spans = mTempObjectForCommitText.getSpans(
- 0, text.length(), CharacterStyle.class);
- for (final CharacterStyle span : spans) {
- final int spanStart = mTempObjectForCommitText.getSpanStart(span);
- final int spanEnd = mTempObjectForCommitText.getSpanEnd(span);
- final int spanFlags = mTempObjectForCommitText.getSpanFlags(span);
- // We have to adjust the end of the span to include an additional character.
- // This is to avoid splitting a unicode surrogate pair.
- // See com.android.inputmethod.latin.common.Constants.UnicodeSurrogate
- // See https://b.corp.google.com/issues/19255233
- if (0 < spanEnd && spanEnd < mTempObjectForCommitText.length()) {
- final char spanEndChar = mTempObjectForCommitText.charAt(spanEnd - 1);
- final char nextChar = mTempObjectForCommitText.charAt(spanEnd);
- if (UnicodeSurrogate.isLowSurrogate(spanEndChar)
- && UnicodeSurrogate.isHighSurrogate(nextChar)) {
- mTempObjectForCommitText.setSpan(span, spanStart, spanEnd + 1, spanFlags);
- }
- }
- }
- mIC.commitText(mTempObjectForCommitText, newCursorPosition);
- }
- }
-
- @Nullable
- public CharSequence getSelectedText(final int flags) {
- return isConnected() ? mIC.getSelectedText(flags) : null;
- }
-
- public boolean canDeleteCharacters() {
- return mExpectedSelStart > 0;
- }
-
- /**
- * Gets the caps modes we should be in after this specific string.
- *
- * This returns a bit set of TextUtils#CAP_MODE_*, masked by the inputType argument.
- * This method also supports faking an additional space after the string passed in argument,
- * to support cases where a space will be added automatically, like in phantom space
- * state for example.
- * Note that for English, we are using American typography rules (which are not specific to
- * American English, it's just the most common set of rules for English).
- *
- * @param inputType a mask of the caps modes to test for.
- * @param spacingAndPunctuations the values of the settings to use for locale and separators.
- * @param hasSpaceBefore if we should consider there should be a space after the string.
- * @return the caps modes that should be on as a set of bits
- */
- public int getCursorCapsMode(final int inputType,
- final SpacingAndPunctuations spacingAndPunctuations, final boolean hasSpaceBefore) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return Constants.TextUtils.CAP_MODE_OFF;
- }
- if (!TextUtils.isEmpty(mComposingText)) {
- if (hasSpaceBefore) {
- // If we have some composing text and a space before, then we should have
- // MODE_CHARACTERS and MODE_WORDS on.
- return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & inputType;
- }
- // We have some composing text - we should be in MODE_CHARACTERS only.
- return TextUtils.CAP_MODE_CHARACTERS & inputType;
- }
- // TODO: this will generally work, but there may be cases where the buffer contains SOME
- // information but not enough to determine the caps mode accurately. This may happen after
- // heavy pressing of delete, for example DEFAULT_TEXT_CACHE_SIZE - 5 times or so.
- // getCapsMode should be updated to be able to return a "not enough info" result so that
- // we can get more context only when needed.
- if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedSelStart) {
- if (!reloadTextCache()) {
- Log.w(TAG, "Unable to connect to the editor. "
- + "Setting caps mode without knowing text.");
- }
- }
- // This never calls InputConnection#getCapsMode - in fact, it's a static method that
- // never blocks or initiates IPC.
- // TODO: don't call #toString() here. Instead, all accesses to
- // mCommittedTextBeforeComposingText should be done on the main thread.
- return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText.toString(), inputType,
- spacingAndPunctuations, hasSpaceBefore);
- }
-
- public int getCodePointBeforeCursor() {
- final int length = mCommittedTextBeforeComposingText.length();
- if (length < 1) return Constants.NOT_A_CODE;
- return Character.codePointBefore(mCommittedTextBeforeComposingText, length);
- }
-
- public CharSequence getTextBeforeCursor(final int n, final int flags) {
- final int cachedLength =
- mCommittedTextBeforeComposingText.length() + mComposingText.length();
- // If we have enough characters to satisfy the request, or if we have all characters in
- // the text field, then we can return the cached version right away.
- // However, if we don't have an expected cursor position, then we should always
- // go fetch the cache again (as it happens, INVALID_CURSOR_POSITION < 0, so we need to
- // test for this explicitly)
- if (INVALID_CURSOR_POSITION != mExpectedSelStart
- && (cachedLength >= n || cachedLength >= mExpectedSelStart)) {
- final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText);
- // We call #toString() here to create a temporary object.
- // In some situations, this method is called on a worker thread, and it's possible
- // the main thread touches the contents of mComposingText while this worker thread
- // is suspended, because mComposingText is a StringBuilder. This may lead to crashes,
- // so we call #toString() on it. That will result in the return value being strictly
- // speaking wrong, but since this is used for basing bigram probability off, and
- // it's only going to matter for one getSuggestions call, it's fine in the practice.
- s.append(mComposingText.toString());
- if (s.length() > n) {
- s.delete(0, s.length() - n);
- }
- return s;
- }
- return getTextBeforeCursorAndDetectLaggyConnection(
- OPERATION_GET_TEXT_BEFORE_CURSOR,
- SLOW_INPUT_CONNECTION_ON_PARTIAL_RELOAD_MS,
- n, flags);
- }
-
- private CharSequence getTextBeforeCursorAndDetectLaggyConnection(
- final int operation, final long timeout, final int n, final int flags) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return null;
- }
- final long startTime = SystemClock.uptimeMillis();
- final CharSequence result = mIC.getTextBeforeCursor(n, flags);
- detectLaggyConnection(operation, timeout, startTime);
- return result;
- }
-
- public CharSequence getTextAfterCursor(final int n, final int flags) {
- return getTextAfterCursorAndDetectLaggyConnection(
- OPERATION_GET_TEXT_AFTER_CURSOR,
- SLOW_INPUT_CONNECTION_ON_PARTIAL_RELOAD_MS,
- n, flags);
- }
-
- private CharSequence getTextAfterCursorAndDetectLaggyConnection(
- final int operation, final long timeout, final int n, final int flags) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return null;
- }
- final long startTime = SystemClock.uptimeMillis();
- final CharSequence result = mIC.getTextAfterCursor(n, flags);
- detectLaggyConnection(operation, timeout, startTime);
- return result;
- }
-
- private void detectLaggyConnection(final int operation, final long timeout, final long startTime) {
- final long duration = SystemClock.uptimeMillis() - startTime;
- if (duration >= timeout) {
- final String operationName = OPERATION_NAMES[operation];
- Log.w(TAG, "Slow InputConnection: " + operationName + " took " + duration + " ms.");
- StatsUtils.onInputConnectionLaggy(operation, duration);
- mLastSlowInputConnectionTime = SystemClock.uptimeMillis();
- }
- }
-
- public void deleteTextBeforeCursor(final int beforeLength) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- // TODO: the following is incorrect if the cursor is not immediately after the composition.
- // Right now we never come here in this case because we reset the composing state before we
- // come here in this case, but we need to fix this.
- final int remainingChars = mComposingText.length() - beforeLength;
- if (remainingChars >= 0) {
- mComposingText.setLength(remainingChars);
- } else {
- mComposingText.setLength(0);
- // Never cut under 0
- final int len = Math.max(mCommittedTextBeforeComposingText.length()
- + remainingChars, 0);
- mCommittedTextBeforeComposingText.setLength(len);
- }
- if (mExpectedSelStart > beforeLength) {
- mExpectedSelStart -= beforeLength;
- mExpectedSelEnd -= beforeLength;
- } else {
- // There are fewer characters before the cursor in the buffer than we are being asked to
- // delete. Only delete what is there, and update the end with the amount deleted.
- mExpectedSelEnd -= mExpectedSelStart;
- mExpectedSelStart = 0;
- }
- if (isConnected()) {
- mIC.deleteSurroundingText(beforeLength, 0);
- }
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- }
-
- public void performEditorAction(final int actionId) {
- mIC = mParent.getCurrentInputConnection();
- if (isConnected()) {
- mIC.performEditorAction(actionId);
- }
- }
-
- public void sendKeyEvent(final KeyEvent keyEvent) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- // This method is only called for enter or backspace when speaking to old applications
- // (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
- // When talking to new applications we never use this method because it's inherently
- // racy and has unpredictable results, but for backward compatibility we continue
- // sending the key events for only Enter and Backspace because some applications
- // mistakenly catch them to do some stuff.
- switch (keyEvent.getKeyCode()) {
- case KeyEvent.KEYCODE_ENTER:
- mCommittedTextBeforeComposingText.append("\n");
- mExpectedSelStart += 1;
- mExpectedSelEnd = mExpectedSelStart;
- break;
- case KeyEvent.KEYCODE_DEL:
- if (0 == mComposingText.length()) {
- if (mCommittedTextBeforeComposingText.length() > 0) {
- mCommittedTextBeforeComposingText.delete(
- mCommittedTextBeforeComposingText.length() - 1,
- mCommittedTextBeforeComposingText.length());
- }
- } else {
- mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
- }
- if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
- // TODO: Handle surrogate pairs.
- mExpectedSelStart -= 1;
- }
- mExpectedSelEnd = mExpectedSelStart;
- break;
- case KeyEvent.KEYCODE_UNKNOWN:
- if (null != keyEvent.getCharacters()) {
- mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
- mExpectedSelStart += keyEvent.getCharacters().length();
- mExpectedSelEnd = mExpectedSelStart;
- }
- break;
- default:
- final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
- mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length();
- mExpectedSelEnd = mExpectedSelStart;
- break;
- }
- }
- if (isConnected()) {
- mIC.sendKeyEvent(keyEvent);
- }
- }
-
- public void setComposingRegion(final int start, final int end) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- final CharSequence textBeforeCursor =
- getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE + (end - start), 0);
- mCommittedTextBeforeComposingText.setLength(0);
- if (!TextUtils.isEmpty(textBeforeCursor)) {
- // The cursor is not necessarily at the end of the composing text, but we have its
- // position in mExpectedSelStart and mExpectedSelEnd. In this case we want the start
- // of the text, so we should use mExpectedSelStart. In other words, the composing
- // text starts (mExpectedSelStart - start) characters before the end of textBeforeCursor
- final int indexOfStartOfComposingText =
- Math.max(textBeforeCursor.length() - (mExpectedSelStart - start), 0);
- mComposingText.append(textBeforeCursor.subSequence(indexOfStartOfComposingText,
- textBeforeCursor.length()));
- mCommittedTextBeforeComposingText.append(
- textBeforeCursor.subSequence(0, indexOfStartOfComposingText));
- }
- if (isConnected()) {
- mIC.setComposingRegion(start, end);
- }
- }
-
- public void setComposingText(final CharSequence text, final int newCursorPosition) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
- mComposingText.setLength(0);
- mComposingText.append(text);
- // TODO: support values of newCursorPosition != 1. At this time, this is never called with
- // newCursorPosition != 1.
- if (isConnected()) {
- mIC.setComposingText(text, newCursorPosition);
- }
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- }
-
- /**
- * Set the selection of the text editor.
- *
- * Calls through to {@link InputConnection#setSelection(int, int)}.
- *
- * @param start the character index where the selection should start.
- * @param end the character index where the selection should end.
- * @return Returns true on success, false on failure: either the input connection is no longer
- * valid when setting the selection or when retrieving the text cache at that point, or
- * invalid arguments were passed.
- */
- public boolean setSelection(final int start, final int end) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- if (start < 0 || end < 0) {
- return false;
- }
- mExpectedSelStart = start;
- mExpectedSelEnd = end;
- if (isConnected()) {
- final boolean isIcValid = mIC.setSelection(start, end);
- if (!isIcValid) {
- return false;
- }
- }
- return reloadTextCache();
- }
-
- public void commitCorrection(final CorrectionInfo correctionInfo) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- // This has no effect on the text field and does not change its content. It only makes
- // TextView flash the text for a second based on indices contained in the argument.
- if (isConnected()) {
- mIC.commitCorrection(correctionInfo);
- }
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- }
-
- public void commitCompletion(final CompletionInfo completionInfo) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- CharSequence text = completionInfo.getText();
- // text should never be null, but just in case, it's better to insert nothing than to crash
- if (null == text) text = "";
- mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
- mComposingText.setLength(0);
- if (isConnected()) {
- mIC.commitCompletion(completionInfo);
- }
- if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- }
-
- @SuppressWarnings("unused")
- @Nonnull
- public NgramContext getNgramContextFromNthPreviousWord(
- final SpacingAndPunctuations spacingAndPunctuations, final int n) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return NgramContext.EMPTY_PREV_WORDS_INFO;
- }
- final CharSequence prev = getTextBeforeCursor(NUM_CHARS_TO_GET_BEFORE_CURSOR, 0);
- if (DEBUG_PREVIOUS_TEXT && null != prev) {
- final int checkLength = NUM_CHARS_TO_GET_BEFORE_CURSOR - 1;
- final String reference = prev.length() <= checkLength ? prev.toString()
- : prev.subSequence(prev.length() - checkLength, prev.length()).toString();
- // TODO: right now the following works because mComposingText holds the part of the
- // composing text that is before the cursor, but this is very confusing. We should
- // fix it.
- final StringBuilder internal = new StringBuilder()
- .append(mCommittedTextBeforeComposingText).append(mComposingText);
- if (internal.length() > checkLength) {
- internal.delete(0, internal.length() - checkLength);
- if (!(reference.equals(internal.toString()))) {
- final String context =
- "Expected text = " + internal + "\nActual text = " + reference;
- ((LatinIME)mParent).debugDumpStateAndCrashWithException(context);
- }
- }
- }
- return NgramContextUtils.getNgramContextFromNthPreviousWord(
- prev, spacingAndPunctuations, n);
- }
-
- private static boolean isPartOfCompositionForScript(final int codePoint,
- final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) {
- // We always consider word connectors part of compositions.
- return spacingAndPunctuations.isWordConnector(codePoint)
- // Otherwise, it's part of composition if it's part of script and not a separator.
- || (!spacingAndPunctuations.isWordSeparator(codePoint)
- && ScriptUtils.isLetterPartOfScript(codePoint, scriptId));
- }
-
- /**
- * Returns the text surrounding the cursor.
- *
- * @param spacingAndPunctuations the rules for spacing and punctuation
- * @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_*
- * @return a range containing the text surrounding the cursor
- */
- public TextRange getWordRangeAtCursor(final SpacingAndPunctuations spacingAndPunctuations,
- final int scriptId) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return null;
- }
- final CharSequence before = getTextBeforeCursorAndDetectLaggyConnection(
- OPERATION_GET_WORD_RANGE_AT_CURSOR,
- SLOW_INPUT_CONNECTION_ON_PARTIAL_RELOAD_MS,
- NUM_CHARS_TO_GET_BEFORE_CURSOR,
- InputConnection.GET_TEXT_WITH_STYLES);
- final CharSequence after = getTextAfterCursorAndDetectLaggyConnection(
- OPERATION_GET_WORD_RANGE_AT_CURSOR,
- SLOW_INPUT_CONNECTION_ON_PARTIAL_RELOAD_MS,
- NUM_CHARS_TO_GET_AFTER_CURSOR,
- InputConnection.GET_TEXT_WITH_STYLES);
- if (before == null || after == null) {
- return null;
- }
-
- // Going backward, find the first breaking point (separator)
- int startIndexInBefore = before.length();
- while (startIndexInBefore > 0) {
- final int codePoint = Character.codePointBefore(before, startIndexInBefore);
- if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
- break;
- }
- --startIndexInBefore;
- if (Character.isSupplementaryCodePoint(codePoint)) {
- --startIndexInBefore;
- }
- }
-
- // Find last word separator after the cursor
- int endIndexInAfter = -1;
- while (++endIndexInAfter < after.length()) {
- final int codePoint = Character.codePointAt(after, endIndexInAfter);
- if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
- break;
- }
- if (Character.isSupplementaryCodePoint(codePoint)) {
- ++endIndexInAfter;
- }
- }
-
- final boolean hasUrlSpans =
- SpannableStringUtils.hasUrlSpans(before, startIndexInBefore, before.length())
- || SpannableStringUtils.hasUrlSpans(after, 0, endIndexInAfter);
- // We don't use TextUtils#concat because it copies all spans without respect to their
- // nature. If the text includes a PARAGRAPH span and it has been split, then
- // TextUtils#concat will crash when it tries to concat both sides of it.
- return new TextRange(
- SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after),
- startIndexInBefore, before.length() + endIndexInAfter, before.length(),
- hasUrlSpans);
- }
-
- public boolean isCursorTouchingWord(final SpacingAndPunctuations spacingAndPunctuations,
- boolean checkTextAfter) {
- if (checkTextAfter && isCursorFollowedByWordCharacter(spacingAndPunctuations)) {
- // If what's after the cursor is a word character, then we're touching a word.
- return true;
- }
- final String textBeforeCursor = mCommittedTextBeforeComposingText.toString();
- int indexOfCodePointInJavaChars = textBeforeCursor.length();
- int consideredCodePoint = 0 == indexOfCodePointInJavaChars ? Constants.NOT_A_CODE
- : textBeforeCursor.codePointBefore(indexOfCodePointInJavaChars);
- // Search for the first non word-connector char
- if (spacingAndPunctuations.isWordConnector(consideredCodePoint)) {
- indexOfCodePointInJavaChars -= Character.charCount(consideredCodePoint);
- consideredCodePoint = 0 == indexOfCodePointInJavaChars ? Constants.NOT_A_CODE
- : textBeforeCursor.codePointBefore(indexOfCodePointInJavaChars);
- }
- return !(Constants.NOT_A_CODE == consideredCodePoint
- || spacingAndPunctuations.isWordSeparator(consideredCodePoint)
- || spacingAndPunctuations.isWordConnector(consideredCodePoint));
- }
-
- public boolean isCursorFollowedByWordCharacter(
- final SpacingAndPunctuations spacingAndPunctuations) {
- final CharSequence after = getTextAfterCursor(1, 0);
- if (TextUtils.isEmpty(after)) {
- return false;
- }
- final int codePointAfterCursor = Character.codePointAt(after, 0);
- if (spacingAndPunctuations.isWordSeparator(codePointAfterCursor)
- || spacingAndPunctuations.isWordConnector(codePointAfterCursor)) {
- return false;
- }
- return true;
- }
-
- public void removeTrailingSpace() {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- final int codePointBeforeCursor = getCodePointBeforeCursor();
- if (Constants.CODE_SPACE == codePointBeforeCursor) {
- deleteTextBeforeCursor(1);
- }
- }
-
- public boolean sameAsTextBeforeCursor(final CharSequence text) {
- final CharSequence beforeText = getTextBeforeCursor(text.length(), 0);
- return TextUtils.equals(text, beforeText);
- }
-
- public boolean revertDoubleSpacePeriod(final SpacingAndPunctuations spacingAndPunctuations) {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- // Here we test whether we indeed have a period and a space before us. This should not
- // be needed, but it's there just in case something went wrong.
- final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0);
- if (!TextUtils.equals(spacingAndPunctuations.mSentenceSeparatorAndSpace,
- textBeforeCursor)) {
- // Theoretically we should not be coming here if there isn't ". " before the
- // cursor, but the application may be changing the text while we are typing, so
- // anything goes. We should not crash.
- Log.d(TAG, "Tried to revert double-space combo but we didn't find \""
- + spacingAndPunctuations.mSentenceSeparatorAndSpace
- + "\" just before the cursor.");
- return false;
- }
- // Double-space results in ". ". A backspace to cancel this should result in a single
- // space in the text field, so we replace ". " with a single space.
- deleteTextBeforeCursor(2);
- final String singleSpace = " ";
- commitText(singleSpace, 1);
- return true;
- }
-
- public boolean revertSwapPunctuation() {
- if (DEBUG_BATCH_NESTING) checkBatchEdit();
- // Here we test whether we indeed have a space and something else before us. This should not
- // be needed, but it's there just in case something went wrong.
- final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0);
- // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
- // enter surrogate pairs this code will have been removed.
- if (TextUtils.isEmpty(textBeforeCursor)
- || (Constants.CODE_SPACE != textBeforeCursor.charAt(1))) {
- // We may only come here if the application is changing the text while we are typing.
- // This is quite a broken case, but not logically impossible, so we shouldn't crash,
- // but some debugging log may be in order.
- Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
- + "find a space just before the cursor.");
- return false;
- }
- deleteTextBeforeCursor(2);
- final String text = " " + textBeforeCursor.subSequence(0, 1);
- commitText(text, 1);
- return true;
- }
-
- /**
- * Heuristic to determine if this is an expected update of the cursor.
- *
- * Sometimes updates to the cursor position are late because of their asynchronous nature.
- * This method tries to determine if this update is one, based on the values of the cursor
- * position in the update, and the currently expected position of the cursor according to
- * LatinIME's internal accounting. If this is not a belated expected update, then it should
- * mean that the user moved the cursor explicitly.
- * This is quite robust, but of course it's not perfect. In particular, it will fail in the
- * case we get an update A, the user types in N characters so as to move the cursor to A+N but
- * we don't get those, and then the user places the cursor between A and A+N, and we get only
- * this update and not the ones in-between. This is almost impossible to achieve even trying
- * very very hard.
- *
- * @param oldSelStart The value of the old selection in the update.
- * @param newSelStart The value of the new selection in the update.
- * @param oldSelEnd The value of the old selection end in the update.
- * @param newSelEnd The value of the new selection end in the update.
- * @return whether this is a belated expected update or not.
- */
- public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart,
- final int oldSelEnd, final int newSelEnd) {
- // This update is "belated" if we are expecting it. That is, mExpectedSelStart and
- // mExpectedSelEnd match the new values that the TextView is updating TO.
- if (mExpectedSelStart == newSelStart && mExpectedSelEnd == newSelEnd) return true;
- // This update is not belated if mExpectedSelStart and mExpectedSelEnd match the old
- // values, and one of newSelStart or newSelEnd is updated to a different value. In this
- // case, it is likely that something other than the IME has moved the selection endpoint
- // to the new value.
- if (mExpectedSelStart == oldSelStart && mExpectedSelEnd == oldSelEnd
- && (oldSelStart != newSelStart || oldSelEnd != newSelEnd)) return false;
- // If neither of the above two cases hold, then the system may be having trouble keeping up
- // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart
- // and mExpectedSelStart, and 3) newSelEnd is between oldSelEnd and mExpectedSelEnd, then
- // assume a belated update.
- return (newSelStart == newSelEnd)
- && (newSelStart - oldSelStart) * (mExpectedSelStart - newSelStart) >= 0
- && (newSelEnd - oldSelEnd) * (mExpectedSelEnd - newSelEnd) >= 0;
- }
-
- /**
- * Looks at the text just before the cursor to find out if it looks like a URL.
- *
- * The weakest point here is, if we don't have enough text bufferized, we may fail to realize
- * we are in URL situation, but other places in this class have the same limitation and it
- * does not matter too much in the practice.
- */
- public boolean textBeforeCursorLooksLikeURL() {
- return StringUtils.lastPartLooksLikeURL(mCommittedTextBeforeComposingText);
- }
-
- /**
- * Looks at the text just before the cursor to find out if we are inside a double quote.
- *
- * As with #textBeforeCursorLooksLikeURL, this is dependent on how much text we have cached.
- * However this won't be a concrete problem in most situations, as the cache is almost always
- * long enough for this use.
- */
- public boolean isInsideDoubleQuoteOrAfterDigit() {
- return StringUtils.isInsideDoubleQuoteOrAfterDigit(mCommittedTextBeforeComposingText);
- }
-
- /**
- * Try to get the text from the editor to expose lies the framework may have been
- * telling us. Concretely, when the device rotates and when the keyboard reopens in the same
- * text field after having been closed with the back key, the frameworks tells us about where
- * the cursor used to be initially in the editor at the time it first received the focus; this
- * may be completely different from the place it is upon rotation. Since we don't have any
- * means to get the real value, try at least to ask the text view for some characters and
- * detect the most damaging cases: when the cursor position is declared to be much smaller
- * than it really is.
- */
- public void tryFixLyingCursorPosition() {
- mIC = mParent.getCurrentInputConnection();
- final CharSequence textBeforeCursor = getTextBeforeCursor(
- Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
- final CharSequence selectedText = isConnected() ? mIC.getSelectedText(0 /* flags */) : null;
- if (null == textBeforeCursor ||
- (!TextUtils.isEmpty(selectedText) && mExpectedSelEnd == mExpectedSelStart)) {
- // If textBeforeCursor is null, we have no idea what kind of text field we have or if
- // thinking about the "cursor position" actually makes any sense. In this case we
- // remember a meaningless cursor position. Contrast this with an empty string, which is
- // valid and should mean the cursor is at the start of the text.
- // Also, if we expect we don't have a selection but we DO have non-empty selected text,
- // then the framework lied to us about the cursor position. In this case, we should just
- // revert to the most basic behavior possible for the next action (backspace in
- // particular comes to mind), so we remember a meaningless cursor position which should
- // result in degraded behavior from the next input.
- // Interestingly, in either case, chances are any action the user takes next will result
- // in a call to onUpdateSelection, which should set things right.
- mExpectedSelStart = mExpectedSelEnd = Constants.NOT_A_CURSOR_POSITION;
- } else {
- final int textLength = textBeforeCursor.length();
- if (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE
- && (textLength > mExpectedSelStart
- || mExpectedSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
- // It should not be possible to have only one of those variables be
- // NOT_A_CURSOR_POSITION, so if they are equal, either the selection is zero-sized
- // (simple cursor, no selection) or there is no cursor/we don't know its pos
- final boolean wasEqual = mExpectedSelStart == mExpectedSelEnd;
- mExpectedSelStart = textLength;
- // We can't figure out the value of mLastSelectionEnd :(
- // But at least if it's smaller than mLastSelectionStart something is wrong,
- // and if they used to be equal we also don't want to make it look like there is a
- // selection.
- if (wasEqual || mExpectedSelStart > mExpectedSelEnd) {
- mExpectedSelEnd = mExpectedSelStart;
- }
- }
- }
- }
-
- @Override
- public boolean performPrivateCommand(final String action, final Bundle data) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return false;
- }
- return mIC.performPrivateCommand(action, data);
- }
-
- public int getExpectedSelectionStart() {
- return mExpectedSelStart;
- }
-
- public int getExpectedSelectionEnd() {
- return mExpectedSelEnd;
- }
-
- /**
- * @return whether there is a selection currently active.
- */
- public boolean hasSelection() {
- return mExpectedSelEnd != mExpectedSelStart;
- }
-
- public boolean isCursorPositionKnown() {
- return INVALID_CURSOR_POSITION != mExpectedSelStart;
- }
-
- /**
- * Work around a bug that was present before Jelly Bean upon rotation.
- *
- * Before Jelly Bean, there is a bug where setComposingRegion and other committing
- * functions on the input connection get ignored until the cursor moves. This method works
- * around the bug by wiggling the cursor first, which reactivates the connection and has
- * the subsequent methods work, then restoring it to its original position.
- *
- * On platforms on which this method is not present, this is a no-op.
- */
- public void maybeMoveTheCursorAroundAndRestoreToWorkaroundABug() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
- if (mExpectedSelStart > 0) {
- mIC.setSelection(mExpectedSelStart - 1, mExpectedSelStart - 1);
- } else {
- mIC.setSelection(mExpectedSelStart + 1, mExpectedSelStart + 1);
- }
- mIC.setSelection(mExpectedSelStart, mExpectedSelEnd);
- }
- }
-
- /**
- * Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}.
- * @param enableMonitor {@code true} to request the editor to call back the method whenever the
- * cursor/anchor position is changed.
- * @param requestImmediateCallback {@code true} to request the editor to call back the method
- * as soon as possible to notify the current cursor/anchor position to the input method.
- * @return {@code true} if the request is accepted. Returns {@code false} otherwise, which
- * includes "not implemented" or "rejected" or "temporarily unavailable" or whatever which
- * prevents the application from fulfilling the request. (TODO: Improve the API when it turns
- * out that we actually need more detailed error codes)
- */
- public boolean requestCursorUpdates(final boolean enableMonitor,
- final boolean requestImmediateCallback) {
- mIC = mParent.getCurrentInputConnection();
- if (!isConnected()) {
- return false;
- }
- return InputConnectionCompatUtils.requestCursorUpdates(
- mIC, enableMonitor, requestImmediateCallback);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
deleted file mode 100644
index 3beb51d68..000000000
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.inputmethodservice.InputMethodService;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.IBinder;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Enrichment class for InputMethodManager to simplify interaction and add functionality.
- */
-// non final for easy mocking.
-public class RichInputMethodManager {
- private static final String TAG = RichInputMethodManager.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private RichInputMethodManager() {
- // This utility class is not publicly instantiable.
- }
-
- private static final RichInputMethodManager sInstance = new RichInputMethodManager();
-
- private Context mContext;
- private InputMethodManagerCompatWrapper mImmWrapper;
- private InputMethodInfoCache mInputMethodInfoCache;
- private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
- private InputMethodInfo mShortcutInputMethodInfo;
- private InputMethodSubtype mShortcutSubtype;
-
- private static final int INDEX_NOT_FOUND = -1;
-
- public static RichInputMethodManager getInstance() {
- sInstance.checkInitialized();
- return sInstance;
- }
-
- public static void init(final Context context) {
- sInstance.initInternal(context);
- }
-
- private boolean isInitialized() {
- return mImmWrapper != null;
- }
-
- private void checkInitialized() {
- if (!isInitialized()) {
- throw new RuntimeException(TAG + " is used before initialization");
- }
- }
-
- private void initInternal(final Context context) {
- if (isInitialized()) {
- return;
- }
- mImmWrapper = new InputMethodManagerCompatWrapper(context);
- mContext = context;
- mInputMethodInfoCache = new InputMethodInfoCache(
- mImmWrapper.mImm, context.getPackageName());
-
- // Initialize additional subtypes.
- SubtypeLocaleUtils.init(context);
- final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes();
- mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
- getInputMethodIdOfThisIme(), additionalSubtypes);
-
- // Initialize the current input method subtype and the shortcut IME.
- refreshSubtypeCaches();
- }
-
- public InputMethodSubtype[] getAdditionalSubtypes() {
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
- final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes(
- prefs, mContext.getResources());
- return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes);
- }
-
- public InputMethodManager getInputMethodManager() {
- checkInitialized();
- return mImmWrapper.mImm;
- }
-
- public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList(
- boolean allowsImplicitlySelectedSubtypes) {
- return getEnabledInputMethodSubtypeList(
- getInputMethodInfoOfThisIme(), allowsImplicitlySelectedSubtypes);
- }
-
- public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
- if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) {
- return true;
- }
- // Was not able to call {@link InputMethodManager#switchToNextInputMethodIBinder,boolean)}
- // because the current device is running ICS or previous and lacks the API.
- if (switchToNextInputSubtypeInThisIme(token, onlyCurrentIme)) {
- return true;
- }
- return switchToNextInputMethodAndSubtype(token);
- }
-
- private boolean switchToNextInputSubtypeInThisIme(final IBinder token,
- final boolean onlyCurrentIme) {
- final InputMethodManager imm = mImmWrapper.mImm;
- final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
- final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList(
- true /* allowsImplicitlySelectedSubtypes */);
- final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes);
- if (currentIndex == INDEX_NOT_FOUND) {
- Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype="
- + SubtypeLocaleUtils.getSubtypeNameForLogging(currentSubtype));
- return false;
- }
- final int nextIndex = (currentIndex + 1) % enabledSubtypes.size();
- if (nextIndex <= currentIndex && !onlyCurrentIme) {
- // The current subtype is the last or only enabled one and it needs to switch to
- // next IME.
- return false;
- }
- final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex);
- setInputMethodAndSubtype(token, nextSubtype);
- return true;
- }
-
- private boolean switchToNextInputMethodAndSubtype(final IBinder token) {
- final InputMethodManager imm = mImmWrapper.mImm;
- final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
- final int currentIndex = getImiIndexInList(getInputMethodInfoOfThisIme(), enabledImis);
- if (currentIndex == INDEX_NOT_FOUND) {
- Log.w(TAG, "Can't find current IME in enabled IMEs: IME package="
- + getInputMethodInfoOfThisIme().getPackageName());
- return false;
- }
- final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis);
- final List<InputMethodSubtype> enabledSubtypes = getEnabledInputMethodSubtypeList(nextImi,
- true /* allowsImplicitlySelectedSubtypes */);
- if (enabledSubtypes.isEmpty()) {
- // The next IME has no subtype.
- imm.setInputMethod(token, nextImi.getId());
- return true;
- }
- final InputMethodSubtype firstSubtype = enabledSubtypes.get(0);
- imm.setInputMethodAndSubtype(token, nextImi.getId(), firstSubtype);
- return true;
- }
-
- private static int getImiIndexInList(final InputMethodInfo inputMethodInfo,
- final List<InputMethodInfo> imiList) {
- final int count = imiList.size();
- for (int index = 0; index < count; index++) {
- final InputMethodInfo imi = imiList.get(index);
- if (imi.equals(inputMethodInfo)) {
- return index;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- // This method mimics {@link InputMethodManager#switchToNextInputMethod(IBinder,boolean)}.
- private static InputMethodInfo getNextNonAuxiliaryIme(final int currentIndex,
- final List<InputMethodInfo> imiList) {
- final int count = imiList.size();
- for (int i = 1; i < count; i++) {
- final int nextIndex = (currentIndex + i) % count;
- final InputMethodInfo nextImi = imiList.get(nextIndex);
- if (!isAuxiliaryIme(nextImi)) {
- return nextImi;
- }
- }
- return imiList.get(currentIndex);
- }
-
- // Copied from {@link InputMethodInfo}. See how auxiliary of IME is determined.
- private static boolean isAuxiliaryIme(final InputMethodInfo imi) {
- final int count = imi.getSubtypeCount();
- if (count == 0) {
- return false;
- }
- for (int index = 0; index < count; index++) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(index);
- if (!subtype.isAuxiliary()) {
- return false;
- }
- }
- return true;
- }
-
- private static class InputMethodInfoCache {
- private final InputMethodManager mImm;
- private final String mImePackageName;
-
- private InputMethodInfo mCachedThisImeInfo;
- private final HashMap<InputMethodInfo, List<InputMethodSubtype>>
- mCachedSubtypeListWithImplicitlySelected;
- private final HashMap<InputMethodInfo, List<InputMethodSubtype>>
- mCachedSubtypeListOnlyExplicitlySelected;
-
- public InputMethodInfoCache(final InputMethodManager imm, final String imePackageName) {
- mImm = imm;
- mImePackageName = imePackageName;
- mCachedSubtypeListWithImplicitlySelected = new HashMap<>();
- mCachedSubtypeListOnlyExplicitlySelected = new HashMap<>();
- }
-
- public synchronized InputMethodInfo getInputMethodOfThisIme() {
- if (mCachedThisImeInfo != null) {
- return mCachedThisImeInfo;
- }
- for (final InputMethodInfo imi : mImm.getInputMethodList()) {
- if (imi.getPackageName().equals(mImePackageName)) {
- mCachedThisImeInfo = imi;
- return imi;
- }
- }
- throw new RuntimeException("Input method id for " + mImePackageName + " not found.");
- }
-
- public synchronized List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
- final InputMethodInfo imi, final boolean allowsImplicitlySelectedSubtypes) {
- final HashMap<InputMethodInfo, List<InputMethodSubtype>> cache =
- allowsImplicitlySelectedSubtypes
- ? mCachedSubtypeListWithImplicitlySelected
- : mCachedSubtypeListOnlyExplicitlySelected;
- final List<InputMethodSubtype> cachedList = cache.get(imi);
- if (cachedList != null) {
- return cachedList;
- }
- final List<InputMethodSubtype> result = mImm.getEnabledInputMethodSubtypeList(
- imi, allowsImplicitlySelectedSubtypes);
- cache.put(imi, result);
- return result;
- }
-
- public synchronized void clear() {
- mCachedThisImeInfo = null;
- mCachedSubtypeListWithImplicitlySelected.clear();
- mCachedSubtypeListOnlyExplicitlySelected.clear();
- }
- }
-
- public InputMethodInfo getInputMethodInfoOfThisIme() {
- return mInputMethodInfoCache.getInputMethodOfThisIme();
- }
-
- public String getInputMethodIdOfThisIme() {
- return getInputMethodInfoOfThisIme().getId();
- }
-
- public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) {
- return checkIfSubtypeBelongsToList(subtype,
- getEnabledInputMethodSubtypeList(
- getInputMethodInfoOfThisIme(),
- true /* allowsImplicitlySelectedSubtypes */));
- }
-
- public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
- final InputMethodSubtype subtype) {
- final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype);
- final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(subtype,
- getMyEnabledInputMethodSubtypeList(false /* allowsImplicitlySelectedSubtypes */));
- return subtypeEnabled && !subtypeExplicitlyEnabled;
- }
-
- private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype,
- final List<InputMethodSubtype> subtypes) {
- return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND;
- }
-
- private static int getSubtypeIndexInList(final InputMethodSubtype subtype,
- final List<InputMethodSubtype> subtypes) {
- final int count = subtypes.size();
- for (int index = 0; index < count; index++) {
- final InputMethodSubtype ims = subtypes.get(index);
- if (ims.equals(subtype)) {
- return index;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
- updateCurrentSubtype(newSubtype);
- updateShortcutIme();
- if (DEBUG) {
- Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging());
- }
- }
-
- private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
-
- @UsedForTesting
- static void forceSubtype(@Nonnull final InputMethodSubtype subtype) {
- sForcedSubtypeForTesting = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
- }
-
- @Nonnull
- public Locale getCurrentSubtypeLocale() {
- if (null != sForcedSubtypeForTesting) {
- return sForcedSubtypeForTesting.getLocale();
- }
- return getCurrentSubtype().getLocale();
- }
-
- @Nonnull
- public RichInputMethodSubtype getCurrentSubtype() {
- if (null != sForcedSubtypeForTesting) {
- return sForcedSubtypeForTesting;
- }
- return mCurrentRichInputMethodSubtype;
- }
-
-
- public String getCombiningRulesExtraValueOfCurrentSubtype() {
- return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
- }
-
- public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList();
- return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
- }
-
- public boolean hasMultipleEnabledSubtypesInThisIme(
- final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> imiList = Collections.singletonList(
- getInputMethodInfoOfThisIme());
- return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
- }
-
- private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
- final List<InputMethodInfo> imiList) {
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : imiList) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- }
- }
-
- if (filteredImisCount > 1) {
- return true;
- }
- final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true);
- int keyboardCount = 0;
- // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
- // both explicitly and implicitly enabled input method subtype.
- // (The current IME should be LatinIME.)
- for (InputMethodSubtype subtype : subtypes) {
- if (KEYBOARD_MODE.equals(subtype.getMode())) {
- ++keyboardCount;
- }
- }
- return keyboardCount > 1;
- }
-
- public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
- final String keyboardLayoutSetName) {
- final InputMethodInfo myImi = getInputMethodInfoOfThisIme();
- final int count = myImi.getSubtypeCount();
- for (int i = 0; i < count; i++) {
- final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
- final String layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
- if (localeString.equals(subtype.getLocale())
- && keyboardLayoutSetName.equals(layoutName)) {
- return subtype;
- }
- }
- return null;
- }
-
- public InputMethodSubtype findSubtypeByLocale(final Locale locale) {
- // Find the best subtype based on a straightforward matching algorithm.
- // TODO: Use LocaleList#getFirstMatch() instead.
- final List<InputMethodSubtype> subtypes =
- getMyEnabledInputMethodSubtypeList(true /* allowsImplicitlySelectedSubtypes */);
- final int count = subtypes.size();
- for (int i = 0; i < count; ++i) {
- final InputMethodSubtype subtype = subtypes.get(i);
- final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
- if (subtypeLocale.equals(locale)) {
- return subtype;
- }
- }
- for (int i = 0; i < count; ++i) {
- final InputMethodSubtype subtype = subtypes.get(i);
- final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
- if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
- subtypeLocale.getCountry().equals(locale.getCountry()) &&
- subtypeLocale.getVariant().equals(locale.getVariant())) {
- return subtype;
- }
- }
- for (int i = 0; i < count; ++i) {
- final InputMethodSubtype subtype = subtypes.get(i);
- final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
- if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
- subtypeLocale.getCountry().equals(locale.getCountry())) {
- return subtype;
- }
- }
- for (int i = 0; i < count; ++i) {
- final InputMethodSubtype subtype = subtypes.get(i);
- final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
- if (subtypeLocale.getLanguage().equals(locale.getLanguage())) {
- return subtype;
- }
- }
- return null;
- }
-
- public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) {
- mImmWrapper.mImm.setInputMethodAndSubtype(
- token, getInputMethodIdOfThisIme(), subtype);
- }
-
- public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) {
- mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
- getInputMethodIdOfThisIme(), subtypes);
- // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of
- // subtypes again next time.
- refreshSubtypeCaches();
- }
-
- private List<InputMethodSubtype> getEnabledInputMethodSubtypeList(final InputMethodInfo imi,
- final boolean allowsImplicitlySelectedSubtypes) {
- return mInputMethodInfoCache.getEnabledInputMethodSubtypeList(
- imi, allowsImplicitlySelectedSubtypes);
- }
-
- public void refreshSubtypeCaches() {
- mInputMethodInfoCache.clear();
- updateCurrentSubtype(mImmWrapper.mImm.getCurrentInputMethodSubtype());
- updateShortcutIme();
- }
-
- public boolean shouldOfferSwitchingToNextInputMethod(final IBinder binder,
- boolean defaultValue) {
- // Use the default value instead on Jelly Bean MR2 and previous where
- // {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod} isn't yet available
- // and on KitKat where the API is still just a stub to return true always.
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- return defaultValue;
- }
- return mImmWrapper.shouldOfferSwitchingToNextInputMethod(binder);
- }
-
- public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
- final Locale systemLocale = mContext.getResources().getConfiguration().locale;
- final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>();
- final InputMethodManager inputMethodManager = getInputMethodManager();
- final List<InputMethodInfo> enabledInputMethodInfoList =
- inputMethodManager.getEnabledInputMethodList();
- for (final InputMethodInfo info : enabledInputMethodInfoList) {
- final List<InputMethodSubtype> enabledSubtypes =
- inputMethodManager.getEnabledInputMethodSubtypeList(
- info, true /* allowsImplicitlySelectedSubtypes */);
- if (enabledSubtypes.isEmpty()) {
- // An IME with no subtypes is found.
- return false;
- }
- enabledSubtypesOfEnabledImes.addAll(enabledSubtypes);
- }
- for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) {
- if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty()
- && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) {
- return false;
- }
- }
- return true;
- }
-
- private void updateCurrentSubtype(@Nullable final InputMethodSubtype subtype) {
- mCurrentRichInputMethodSubtype = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
- }
-
- private void updateShortcutIme() {
- if (DEBUG) {
- Log.d(TAG, "Update shortcut IME from : "
- + (mShortcutInputMethodInfo == null
- ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
- + (mShortcutSubtype == null ? "<null>" : (
- mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
- }
- final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype;
- final boolean implicitlyEnabledSubtype = checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
- richSubtype.getRawSubtype());
- final Locale systemLocale = mContext.getResources().getConfiguration().locale;
- LanguageOnSpacebarUtils.onSubtypeChanged(
- richSubtype, implicitlyEnabledSubtype, systemLocale);
- LanguageOnSpacebarUtils.setEnabledSubtypes(getMyEnabledInputMethodSubtypeList(
- true /* allowsImplicitlySelectedSubtypes */));
-
- // TODO: Update an icon for shortcut IME
- final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
- getInputMethodManager().getShortcutInputMethodsAndSubtypes();
- mShortcutInputMethodInfo = null;
- mShortcutSubtype = null;
- for (final InputMethodInfo imi : shortcuts.keySet()) {
- final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
- // TODO: Returns the first found IMI for now. Should handle all shortcuts as
- // appropriate.
- mShortcutInputMethodInfo = imi;
- // TODO: Pick up the first found subtype for now. Should handle all subtypes
- // as appropriate.
- mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
- break;
- }
- if (DEBUG) {
- Log.d(TAG, "Update shortcut IME to : "
- + (mShortcutInputMethodInfo == null
- ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
- + (mShortcutSubtype == null ? "<null>" : (
- mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
- }
- }
-
- public void switchToShortcutIme(final InputMethodService context) {
- if (mShortcutInputMethodInfo == null) {
- return;
- }
-
- final String imiId = mShortcutInputMethodInfo.getId();
- switchToTargetIME(imiId, mShortcutSubtype, context);
- }
-
- private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
- final InputMethodService context) {
- final IBinder token = context.getWindow().getWindow().getAttributes().token;
- if (token == null) {
- return;
- }
- final InputMethodManager imm = getInputMethodManager();
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- imm.setInputMethodAndSubtype(token, imiId, subtype);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- public boolean isShortcutImeReady() {
- if (mShortcutInputMethodInfo == null) {
- return false;
- }
- if (mShortcutSubtype == null) {
- return true;
- }
- return true;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
deleted file mode 100644
index 941149895..000000000
--- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
-
-import android.os.Build;
-import android.util.Log;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.compat.BuildCompatUtils;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Enrichment class for InputMethodSubtype to enable concurrent multi-lingual input.
- *
- * Right now, this returns the extra value of its primary subtype.
- */
-// non final for easy mocking.
-public class RichInputMethodSubtype {
- private static final String TAG = RichInputMethodSubtype.class.getSimpleName();
-
- private static final HashMap<Locale, Locale> sLocaleMap = initializeLocaleMap();
- private static final HashMap<Locale, Locale> initializeLocaleMap() {
- final HashMap<Locale, Locale> map = new HashMap<>();
- if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- // Locale#forLanguageTag is available on API Level 21+.
- // TODO: Remove this workaround once when we become able to deal with "sr-Latn".
- map.put(Locale.forLanguageTag("sr-Latn"), new Locale("sr_ZZ"));
- }
- return map;
- }
-
- @Nonnull
- private final InputMethodSubtype mSubtype;
- @Nonnull
- private final Locale mLocale;
- @Nonnull
- private final Locale mOriginalLocale;
-
- public RichInputMethodSubtype(@Nonnull final InputMethodSubtype subtype) {
- mSubtype = subtype;
- mOriginalLocale = InputMethodSubtypeCompatUtils.getLocaleObject(mSubtype);
- final Locale mappedLocale = sLocaleMap.get(mOriginalLocale);
- mLocale = mappedLocale != null ? mappedLocale : mOriginalLocale;
- }
-
- // Extra values are determined by the primary subtype. This is probably right, but
- // we may have to revisit this later.
- public String getExtraValueOf(@Nonnull final String key) {
- return mSubtype.getExtraValueOf(key);
- }
-
- // The mode is also determined by the primary subtype.
- public String getMode() {
- return mSubtype.getMode();
- }
-
- public boolean isNoLanguage() {
- return SubtypeLocaleUtils.NO_LANGUAGE.equals(mSubtype.getLocale());
- }
-
- public String getNameForLogging() {
- return toString();
- }
-
- // InputMethodSubtype's display name for spacebar text in its locale.
- // isAdditionalSubtype (T=true, F=false)
- // locale layout | Middle Full
- // ------ ------- - --------- ----------------------
- // en_US qwerty F English English (US) exception
- // en_GB qwerty F English English (UK) exception
- // es_US spanish F Español Español (EE.UU.) exception
- // fr azerty F Français Français
- // fr_CA qwerty F Français Français (Canada)
- // fr_CH swiss F Français Français (Suisse)
- // de qwertz F Deutsch Deutsch
- // de_CH swiss T Deutsch Deutsch (Schweiz)
- // zz qwerty F QWERTY QWERTY
- // fr qwertz T Français Français
- // de qwerty T Deutsch Deutsch
- // en_US azerty T English English (US)
- // zz azerty T AZERTY AZERTY
- // Get the RichInputMethodSubtype's full display name in its locale.
- @Nonnull
- public String getFullDisplayName() {
- if (isNoLanguage()) {
- return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
- }
- return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(mSubtype.getLocale());
- }
-
- // Get the RichInputMethodSubtype's middle display name in its locale.
- @Nonnull
- public String getMiddleDisplayName() {
- if (isNoLanguage()) {
- return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
- }
- return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(mSubtype.getLocale());
- }
-
- @Override
- public boolean equals(final Object o) {
- if (!(o instanceof RichInputMethodSubtype)) {
- return false;
- }
- final RichInputMethodSubtype other = (RichInputMethodSubtype)o;
- return mSubtype.equals(other.mSubtype) && mLocale.equals(other.mLocale);
- }
-
- @Override
- public int hashCode() {
- return mSubtype.hashCode() + mLocale.hashCode();
- }
-
- @Override
- public String toString() {
- return "Multi-lingual subtype: " + mSubtype + ", " + mLocale;
- }
-
- @Nonnull
- public Locale getLocale() {
- return mLocale;
- }
-
- @Nonnull
- public Locale getOriginalLocale() {
- return mOriginalLocale;
- }
-
- public boolean isRtlSubtype() {
- // The subtype is considered RTL if the language of the main subtype is RTL.
- return LocaleUtils.isRtlLanguage(mLocale);
- }
-
- // TODO: remove this method
- @Nonnull
- public InputMethodSubtype getRawSubtype() { return mSubtype; }
-
- @Nonnull
- public String getKeyboardLayoutSetName() {
- return SubtypeLocaleUtils.getKeyboardLayoutSetName(mSubtype);
- }
-
- public static RichInputMethodSubtype getRichInputMethodSubtype(
- @Nullable final InputMethodSubtype subtype) {
- if (subtype == null) {
- return getNoLanguageSubtype();
- } else {
- return new RichInputMethodSubtype(subtype);
- }
- }
-
- // Placeholer for no language QWERTY subtype. See {@link R.xml.method}.
- private static final int SUBTYPE_ID_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
- private static final String EXTRA_VALUE_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE =
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
- + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
- @Nonnull
- private static final RichInputMethodSubtype PLACEHOLDER_NO_LANGUAGE_SUBTYPE =
- new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype(
- R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
- SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
- EXTRA_VALUE_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE,
- false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
- SUBTYPE_ID_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE));
- // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
- // Placeholder Emoji subtype. See {@link R.xml.method}.
- private static final int SUBTYPE_ID_OF_PLACEHOLDER_EMOJI_SUBTYPE = 0xd78b2ed0;
- private static final String EXTRA_VALUE_OF_PLACEHOLDER_EMOJI_SUBTYPE =
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
- @Nonnull
- private static final RichInputMethodSubtype PLACEHOLDER_EMOJI_SUBTYPE = new RichInputMethodSubtype(
- InputMethodSubtypeCompatUtils.newInputMethodSubtype(
- R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
- SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
- EXTRA_VALUE_OF_PLACEHOLDER_EMOJI_SUBTYPE,
- false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
- SUBTYPE_ID_OF_PLACEHOLDER_EMOJI_SUBTYPE));
- private static RichInputMethodSubtype sNoLanguageSubtype;
- private static RichInputMethodSubtype sEmojiSubtype;
-
- @Nonnull
- public static RichInputMethodSubtype getNoLanguageSubtype() {
- RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype;
- if (noLanguageSubtype == null) {
- final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance()
- .findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY);
- if (rawNoLanguageSubtype != null) {
- noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype);
- }
- }
- if (noLanguageSubtype != null) {
- sNoLanguageSubtype = noLanguageSubtype;
- return noLanguageSubtype;
- }
- Log.w(TAG, "Can't find any language with QWERTY subtype");
- Log.w(TAG, "No input method subtype found; returning placeholder subtype: "
- + PLACEHOLDER_NO_LANGUAGE_SUBTYPE);
- return PLACEHOLDER_NO_LANGUAGE_SUBTYPE;
- }
-
- @Nonnull
- public static RichInputMethodSubtype getEmojiSubtype() {
- RichInputMethodSubtype emojiSubtype = sEmojiSubtype;
- if (emojiSubtype == null) {
- final InputMethodSubtype rawEmojiSubtype = RichInputMethodManager.getInstance()
- .findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
- if (rawEmojiSubtype != null) {
- emojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype);
- }
- }
- if (emojiSubtype != null) {
- sEmojiSubtype = emojiSubtype;
- return emojiSubtype;
- }
- Log.w(TAG, "Can't find emoji subtype");
- Log.w(TAG, "No input method subtype found; returning placeholder subtype: "
- + PLACEHOLDER_EMOJI_SUBTYPE);
- return PLACEHOLDER_EMOJI_SUBTYPE;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
deleted file mode 100644
index da23617af..000000000
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.text.TextUtils;
-
-import static com.android.inputmethod.latin.define.DecoderSpecificConstants.SHOULD_AUTO_CORRECT_USING_NON_WHITE_LISTED_SUGGESTION;
-import static com.android.inputmethod.latin.define.DecoderSpecificConstants.SHOULD_REMOVE_PREVIOUSLY_REJECTED_SUGGESTION;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
-import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.SuggestionResults;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-
-/**
- * This class loads a dictionary and provides a list of suggestions for a given sequence of
- * characters. This includes corrections and completions.
- */
-public final class Suggest {
- public static final String TAG = Suggest.class.getSimpleName();
-
- // Session id for
- // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}.
- // We are sharing the same ID between typing and gesture to save RAM footprint.
- public static final int SESSION_ID_TYPING = 0;
- public static final int SESSION_ID_GESTURE = 0;
-
- // Close to -2**31
- private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000;
-
- private static final boolean DBG = DebugFlags.DEBUG_ENABLED;
- private final DictionaryFacilitator mDictionaryFacilitator;
-
- private static final int MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN = 12;
- private static final HashMap<String, Integer> sLanguageToMaximumAutoCorrectionWithSpaceLength =
- new HashMap<>();
- static {
- // TODO: should we add Finnish here?
- // TODO: This should not be hardcoded here but be written in the dictionary header
- sLanguageToMaximumAutoCorrectionWithSpaceLength.put(Locale.GERMAN.getLanguage(),
- MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN);
- }
-
- private float mAutoCorrectionThreshold;
- private float mPlausibilityThreshold;
-
- public Suggest(final DictionaryFacilitator dictionaryFacilitator) {
- mDictionaryFacilitator = dictionaryFacilitator;
- }
-
- /**
- * Set the normalized-score threshold for a suggestion to be considered strong enough that we
- * will auto-correct to this.
- * @param threshold the threshold
- */
- public void setAutoCorrectionThreshold(final float threshold) {
- mAutoCorrectionThreshold = threshold;
- }
-
- /**
- * Set the normalized-score threshold for what we consider a "plausible" suggestion, in
- * the same dimension as the auto-correction threshold.
- * @param threshold the threshold
- */
- public void setPlausibilityThreshold(final float threshold) {
- mPlausibilityThreshold = threshold;
- }
-
- public interface OnGetSuggestedWordsCallback {
- public void onGetSuggestedWords(final SuggestedWords suggestedWords);
- }
-
- public void getSuggestedWords(final WordComposer wordComposer,
- final NgramContext ngramContext, final Keyboard keyboard,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final boolean isCorrectionEnabled, final int inputStyle, final int sequenceNumber,
- final OnGetSuggestedWordsCallback callback) {
- if (wordComposer.isBatchMode()) {
- getSuggestedWordsForBatchInput(wordComposer, ngramContext, keyboard,
- settingsValuesForSuggestion, inputStyle, sequenceNumber, callback);
- } else {
- getSuggestedWordsForNonBatchInput(wordComposer, ngramContext, keyboard,
- settingsValuesForSuggestion, inputStyle, isCorrectionEnabled,
- sequenceNumber, callback);
- }
- }
-
- private static ArrayList<SuggestedWordInfo> getTransformedSuggestedWordInfoList(
- final WordComposer wordComposer, final SuggestionResults results,
- final int trailingSingleQuotesCount, final Locale defaultLocale) {
- final boolean shouldMakeSuggestionsAllUpperCase = wordComposer.isAllUpperCase()
- && !wordComposer.isResumed();
- final boolean isOnlyFirstCharCapitalized =
- wordComposer.isOrWillBeOnlyFirstCharCapitalized();
-
- final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(results);
- final int suggestionsCount = suggestionsContainer.size();
- if (isOnlyFirstCharCapitalized || shouldMakeSuggestionsAllUpperCase
- || 0 != trailingSingleQuotesCount) {
- for (int i = 0; i < suggestionsCount; ++i) {
- final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
- final Locale wordLocale = wordInfo.mSourceDict.mLocale;
- final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
- wordInfo, null == wordLocale ? defaultLocale : wordLocale,
- shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized,
- trailingSingleQuotesCount);
- suggestionsContainer.set(i, transformedWordInfo);
- }
- }
- return suggestionsContainer;
- }
-
- private static SuggestedWordInfo getWhitelistedWordInfoOrNull(
- @Nonnull final ArrayList<SuggestedWordInfo> suggestions) {
- if (suggestions.isEmpty()) {
- return null;
- }
- final SuggestedWordInfo firstSuggestedWordInfo = suggestions.get(0);
- if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
- return null;
- }
- return firstSuggestedWordInfo;
- }
-
- // Retrieves suggestions for non-batch input (typing, recorrection, predictions...)
- // and calls the callback function with the suggestions.
- private void getSuggestedWordsForNonBatchInput(final WordComposer wordComposer,
- final NgramContext ngramContext, final Keyboard keyboard,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int inputStyleIfNotPrediction, final boolean isCorrectionEnabled,
- final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
- final String typedWordString = wordComposer.getTypedWord();
- final int trailingSingleQuotesCount =
- StringUtils.getTrailingSingleQuotesCount(typedWordString);
- final String consideredWord = trailingSingleQuotesCount > 0
- ? typedWordString.substring(0, typedWordString.length() - trailingSingleQuotesCount)
- : typedWordString;
-
- final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
- wordComposer.getComposedDataSnapshot(), ngramContext, keyboard,
- settingsValuesForSuggestion, SESSION_ID_TYPING, inputStyleIfNotPrediction);
- final Locale locale = mDictionaryFacilitator.getLocale();
- final ArrayList<SuggestedWordInfo> suggestionsContainer =
- getTransformedSuggestedWordInfoList(wordComposer, suggestionResults,
- trailingSingleQuotesCount, locale);
-
- boolean foundInDictionary = false;
- Dictionary sourceDictionaryOfRemovedWord = null;
- for (final SuggestedWordInfo info : suggestionsContainer) {
- // Search for the best dictionary, defined as the first one with the highest match
- // quality we can find.
- if (!foundInDictionary && typedWordString.equals(info.mWord)) {
- // Use this source if the old match had lower quality than this match
- sourceDictionaryOfRemovedWord = info.mSourceDict;
- foundInDictionary = true;
- break;
- }
- }
-
- final int firstOcurrenceOfTypedWordInSuggestions =
- SuggestedWordInfo.removeDups(typedWordString, suggestionsContainer);
-
- final SuggestedWordInfo whitelistedWordInfo =
- getWhitelistedWordInfoOrNull(suggestionsContainer);
- final String whitelistedWord = whitelistedWordInfo == null
- ? null : whitelistedWordInfo.mWord;
- final boolean resultsArePredictions = !wordComposer.isComposingWord();
-
- // We allow auto-correction if whitelisting is not required or the word is whitelisted,
- // or if the word had more than one char and was not suggested.
- final boolean allowsToBeAutoCorrected =
- (SHOULD_AUTO_CORRECT_USING_NON_WHITE_LISTED_SUGGESTION || whitelistedWord != null)
- || (consideredWord.length() > 1 && (sourceDictionaryOfRemovedWord == null));
-
- final boolean hasAutoCorrection;
- // If correction is not enabled, we never auto-correct. This is for example for when
- // the setting "Auto-correction" is "off": we still suggest, but we don't auto-correct.
- if (!isCorrectionEnabled
- // If the word does not allow to be auto-corrected, then we don't auto-correct.
- || !allowsToBeAutoCorrected
- // If we are doing prediction, then we never auto-correct of course
- || resultsArePredictions
- // If we don't have suggestion results, we can't evaluate the first suggestion
- // for auto-correction
- || suggestionResults.isEmpty()
- // If the word has digits, we never auto-correct because it's likely the word
- // was type with a lot of care
- || wordComposer.hasDigits()
- // If the word is mostly caps, we never auto-correct because this is almost
- // certainly intentional (and careful input)
- || wordComposer.isMostlyCaps()
- // We never auto-correct when suggestions are resumed because it would be unexpected
- || wordComposer.isResumed()
- // If we don't have a main dictionary, we never want to auto-correct. The reason
- // for this is, the user may have a contact whose name happens to match a valid
- // word in their language, and it will unexpectedly auto-correct. For example, if
- // the user types in English with no dictionary and has a "Will" in their contact
- // list, "will" would always auto-correct to "Will" which is unwanted. Hence, no
- // main dict => no auto-correct. Also, it would probably get obnoxious quickly.
- // TODO: now that we have personalization, we may want to re-evaluate this decision
- || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()
- // If the first suggestion is a shortcut we never auto-correct to it, regardless
- // of how strong it is (allowlist entries are not KIND_SHORTCUT but KIND_WHITELIST).
- // TODO: we may want to have shortcut-only entries auto-correct in the future.
- || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) {
- hasAutoCorrection = false;
- } else {
- final SuggestedWordInfo firstSuggestion = suggestionResults.first();
- if (suggestionResults.mFirstSuggestionExceedsConfidenceThreshold
- && firstOcurrenceOfTypedWordInSuggestions != 0) {
- hasAutoCorrection = true;
- } else if (!AutoCorrectionUtils.suggestionExceedsThreshold(
- firstSuggestion, consideredWord, mAutoCorrectionThreshold)) {
- // Score is too low for autocorrect
- hasAutoCorrection = false;
- } else {
- // We have a high score, so we need to check if this suggestion is in the correct
- // form to allow auto-correcting to it in this language. For details of how this
- // is determined, see #isAllowedByAutoCorrectionWithSpaceFilter.
- // TODO: this should not have its own logic here but be handled by the dictionary.
- hasAutoCorrection = isAllowedByAutoCorrectionWithSpaceFilter(firstSuggestion);
- }
- }
-
- final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(typedWordString,
- "" /* prevWordsContext */, SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_TYPED,
- null == sourceDictionaryOfRemovedWord ? Dictionary.DICTIONARY_USER_TYPED
- : sourceDictionaryOfRemovedWord,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
- if (!TextUtils.isEmpty(typedWordString)) {
- suggestionsContainer.add(0, typedWordInfo);
- }
-
- final ArrayList<SuggestedWordInfo> suggestionsList;
- if (DBG && !suggestionsContainer.isEmpty()) {
- suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWordString,
- suggestionsContainer);
- } else {
- suggestionsList = suggestionsContainer;
- }
-
- final int inputStyle;
- if (resultsArePredictions) {
- inputStyle = suggestionResults.mIsBeginningOfSentence
- ? SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION
- : SuggestedWords.INPUT_STYLE_PREDICTION;
- } else {
- inputStyle = inputStyleIfNotPrediction;
- }
-
- final boolean isTypedWordValid = firstOcurrenceOfTypedWordInSuggestions > -1
- || (!resultsArePredictions && !allowsToBeAutoCorrected);
- callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
- suggestionResults.mRawSuggestions, typedWordInfo,
- isTypedWordValid,
- hasAutoCorrection /* willAutoCorrect */,
- false /* isObsoleteSuggestions */, inputStyle, sequenceNumber));
- }
-
- // Retrieves suggestions for the batch input
- // and calls the callback function with the suggestions.
- private void getSuggestedWordsForBatchInput(final WordComposer wordComposer,
- final NgramContext ngramContext, final Keyboard keyboard,
- final SettingsValuesForSuggestion settingsValuesForSuggestion,
- final int inputStyle, final int sequenceNumber,
- final OnGetSuggestedWordsCallback callback) {
- final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
- wordComposer.getComposedDataSnapshot(), ngramContext, keyboard,
- settingsValuesForSuggestion, SESSION_ID_GESTURE, inputStyle);
- // For transforming words that don't come from a dictionary, because it's our best bet
- final Locale locale = mDictionaryFacilitator.getLocale();
- final ArrayList<SuggestedWordInfo> suggestionsContainer =
- new ArrayList<>(suggestionResults);
- final int suggestionsCount = suggestionsContainer.size();
- final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock();
- final boolean isAllUpperCase = wordComposer.isAllUpperCase();
- if (isFirstCharCapitalized || isAllUpperCase) {
- for (int i = 0; i < suggestionsCount; ++i) {
- final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
- final Locale wordlocale = wordInfo.mSourceDict.mLocale;
- final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
- wordInfo, null == wordlocale ? locale : wordlocale, isAllUpperCase,
- isFirstCharCapitalized, 0 /* trailingSingleQuotesCount */);
- suggestionsContainer.set(i, transformedWordInfo);
- }
- }
-
- if (SHOULD_REMOVE_PREVIOUSLY_REJECTED_SUGGESTION
- && suggestionsContainer.size() > 1
- && TextUtils.equals(suggestionsContainer.get(0).mWord,
- wordComposer.getRejectedBatchModeSuggestion())) {
- final SuggestedWordInfo rejected = suggestionsContainer.remove(0);
- suggestionsContainer.add(1, rejected);
- }
- SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer);
-
- // For some reason some suggestions with MIN_VALUE are making their way here.
- // TODO: Find a more robust way to detect distracters.
- for (int i = suggestionsContainer.size() - 1; i >= 0; --i) {
- if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) {
- suggestionsContainer.remove(i);
- }
- }
-
- // In the batch input mode, the most relevant suggested word should act as a "typed word"
- // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
- // Note that because this method is never used to get predictions, there is no need to
- // modify inputType such in getSuggestedWordsForNonBatchInput.
- final SuggestedWordInfo pseudoTypedWordInfo = suggestionsContainer.isEmpty() ? null
- : suggestionsContainer.get(0);
-
- callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer,
- suggestionResults.mRawSuggestions,
- pseudoTypedWordInfo,
- true /* typedWordValid */,
- false /* willAutoCorrect */,
- false /* isObsoleteSuggestions */,
- inputStyle, sequenceNumber));
- }
-
- private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
- final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) {
- final SuggestedWordInfo typedWordInfo = suggestions.get(0);
- typedWordInfo.setDebugString("+");
- final int suggestionsSize = suggestions.size();
- final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>(suggestionsSize);
- suggestionsList.add(typedWordInfo);
- // Note: i here is the index in mScores[], but the index in mSuggestions is one more
- // than i because we added the typed word to mSuggestions without touching mScores.
- for (int i = 0; i < suggestionsSize - 1; ++i) {
- final SuggestedWordInfo cur = suggestions.get(i + 1);
- final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore(
- typedWord, cur.toString(), cur.mScore);
- final String scoreInfoString;
- if (normalizedScore > 0) {
- scoreInfoString = String.format(
- Locale.ROOT, "%d (%4.2f), %s", cur.mScore, normalizedScore,
- cur.mSourceDict.mDictType);
- } else {
- scoreInfoString = Integer.toString(cur.mScore);
- }
- cur.setDebugString(scoreInfoString);
- suggestionsList.add(cur);
- }
- return suggestionsList;
- }
-
- /**
- * Computes whether this suggestion should be blocked or not in this language
- *
- * This function implements a filter that avoids auto-correcting to suggestions that contain
- * spaces that are above a certain language-dependent character limit. In languages like German
- * where it's possible to concatenate many words, it often happens our dictionary does not
- * have the longer words. In this case, we offer a lot of unhelpful suggestions that contain
- * one or several spaces. Ideally we should understand what the user wants and display useful
- * suggestions by improving the dictionary and possibly having some specific logic. Until
- * that's possible we should avoid displaying unhelpful suggestions. But it's hard to tell
- * whether a suggestion is useful or not. So at least for the time being we block
- * auto-correction when the suggestion is long and contains a space, which should avoid the
- * worst damage.
- * This function is implementing that filter. If the language enforces no such limit, then it
- * always returns true. If the suggestion contains no space, it also returns true. Otherwise,
- * it checks the length against the language-specific limit.
- *
- * @param info the suggestion info
- * @return whether it's fine to auto-correct to this.
- */
- private static boolean isAllowedByAutoCorrectionWithSpaceFilter(final SuggestedWordInfo info) {
- final Locale locale = info.mSourceDict.mLocale;
- if (null == locale) {
- return true;
- }
- final Integer maximumLengthForThisLanguage =
- sLanguageToMaximumAutoCorrectionWithSpaceLength.get(locale.getLanguage());
- if (null == maximumLengthForThisLanguage) {
- // This language does not enforce a maximum length to auto-correction
- return true;
- }
- return info.mWord.length() <= maximumLengthForThisLanguage
- || -1 == info.mWord.indexOf(Constants.CODE_SPACE);
- }
-
- /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
- final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
- final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) {
- final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
- if (isAllUpperCase) {
- sb.append(wordInfo.mWord.toUpperCase(locale));
- } else if (isOnlyFirstCharCapitalized) {
- sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale));
- } else {
- sb.append(wordInfo.mWord);
- }
- // Appending quotes is here to help people quote words. However, it's not helpful
- // when they type words with quotes toward the end like "it's" or "didn't", where
- // it's more likely the user missed the last character (or didn't type it yet).
- final int quotesToAppend = trailingSingleQuotesCount
- - (-1 == wordInfo.mWord.indexOf(Constants.CODE_SINGLE_QUOTE) ? 0 : 1);
- for (int i = quotesToAppend - 1; i >= 0; --i) {
- sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE);
- }
- return new SuggestedWordInfo(sb.toString(), wordInfo.mPrevWordsContext,
- wordInfo.mScore, wordInfo.mKindAndFlags,
- wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord,
- wordInfo.mAutoCommitFirstWordConfidence);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
deleted file mode 100644
index bcd4d5f69..000000000
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.text.TextUtils;
-import android.view.inputmethod.CompletionInfo;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DebugFlags;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class SuggestedWords {
- public static final int INDEX_OF_TYPED_WORD = 0;
- public static final int INDEX_OF_AUTO_CORRECTION = 1;
- public static final int NOT_A_SEQUENCE_NUMBER = -1;
-
- public static final int INPUT_STYLE_NONE = 0;
- public static final int INPUT_STYLE_TYPING = 1;
- public static final int INPUT_STYLE_UPDATE_BATCH = 2;
- public static final int INPUT_STYLE_TAIL_BATCH = 3;
- public static final int INPUT_STYLE_APPLICATION_SPECIFIED = 4;
- public static final int INPUT_STYLE_RECORRECTION = 5;
- public static final int INPUT_STYLE_PREDICTION = 6;
- public static final int INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION = 7;
-
- // The maximum number of suggestions available.
- public static final int MAX_SUGGESTIONS = 18;
-
- private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0);
- @Nonnull
- private static final SuggestedWords EMPTY = new SuggestedWords(
- EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, null /* typedWord */,
- false /* typedWordValid */, false /* willAutoCorrect */,
- false /* isObsoleteSuggestions */, INPUT_STYLE_NONE, NOT_A_SEQUENCE_NUMBER);
-
- @Nullable
- public final SuggestedWordInfo mTypedWordInfo;
- public final boolean mTypedWordValid;
- // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition
- // of what this flag means would be "the top suggestion is strong enough to auto-correct",
- // whether this exactly matches the user entry or not.
- public final boolean mWillAutoCorrect;
- public final boolean mIsObsoleteSuggestions;
- // How the input for these suggested words was done by the user. Must be one of the
- // INPUT_STYLE_* constants above.
- public final int mInputStyle;
- public final int mSequenceNumber; // Sequence number for auto-commit.
- @Nonnull
- protected final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
- @Nullable
- public final ArrayList<SuggestedWordInfo> mRawSuggestions;
-
- public SuggestedWords(@Nonnull final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
- @Nullable final ArrayList<SuggestedWordInfo> rawSuggestions,
- @Nullable final SuggestedWordInfo typedWordInfo,
- final boolean typedWordValid,
- final boolean willAutoCorrect,
- final boolean isObsoleteSuggestions,
- final int inputStyle,
- final int sequenceNumber) {
- mSuggestedWordInfoList = suggestedWordInfoList;
- mRawSuggestions = rawSuggestions;
- mTypedWordValid = typedWordValid;
- mWillAutoCorrect = willAutoCorrect;
- mIsObsoleteSuggestions = isObsoleteSuggestions;
- mInputStyle = inputStyle;
- mSequenceNumber = sequenceNumber;
- mTypedWordInfo = typedWordInfo;
- }
-
- public boolean isEmpty() {
- return mSuggestedWordInfoList.isEmpty();
- }
-
- public int size() {
- return mSuggestedWordInfoList.size();
- }
-
- /**
- * Get suggested word to show as suggestions to UI.
- *
- * @param shouldShowLxxSuggestionUi true if showing suggestion UI introduced in LXX and later.
- * @return the count of suggested word to show as suggestions to UI.
- */
- public int getWordCountToShow(final boolean shouldShowLxxSuggestionUi) {
- if (isPrediction() || !shouldShowLxxSuggestionUi) {
- return size();
- }
- return size() - /* typed word */ 1;
- }
-
- /**
- * Get {@link SuggestedWordInfo} object for the typed word.
- * @return The {@link SuggestedWordInfo} object for the typed word.
- */
- public SuggestedWordInfo getTypedWordInfo() {
- return mTypedWordInfo;
- }
-
- /**
- * Get suggested word at <code>index</code>.
- * @param index The index of the suggested word.
- * @return The suggested word.
- */
- public String getWord(final int index) {
- return mSuggestedWordInfoList.get(index).mWord;
- }
-
- /**
- * Get displayed text at <code>index</code>.
- * In RTL languages, the displayed text on the suggestion strip may be different from the
- * suggested word that is returned from {@link #getWord(int)}. For example the displayed text
- * of punctuation suggestion "(" should be ")".
- * @param index The index of the text to display.
- * @return The text to be displayed.
- */
- public String getLabel(final int index) {
- return mSuggestedWordInfoList.get(index).mWord;
- }
-
- /**
- * Get {@link SuggestedWordInfo} object at <code>index</code>.
- * @param index The index of the {@link SuggestedWordInfo}.
- * @return The {@link SuggestedWordInfo} object.
- */
- public SuggestedWordInfo getInfo(final int index) {
- return mSuggestedWordInfoList.get(index);
- }
-
- /**
- * Gets the suggestion index from the suggestions list.
- * @param suggestedWordInfo The {@link SuggestedWordInfo} to find the index.
- * @return The position of the suggestion in the suggestion list.
- */
- public int indexOf(SuggestedWordInfo suggestedWordInfo) {
- return mSuggestedWordInfoList.indexOf(suggestedWordInfo);
- }
-
- public String getDebugString(final int pos) {
- if (!DebugFlags.DEBUG_ENABLED) {
- return null;
- }
- final SuggestedWordInfo wordInfo = getInfo(pos);
- if (wordInfo == null) {
- return null;
- }
- final String debugString = wordInfo.getDebugString();
- if (TextUtils.isEmpty(debugString)) {
- return null;
- }
- return debugString;
- }
-
- /**
- * The predicator to tell whether this object represents punctuation suggestions.
- * @return false if this object desn't represent punctuation suggestions.
- */
- public boolean isPunctuationSuggestions() {
- return false;
- }
-
- @Override
- public String toString() {
- // Pretty-print method to help debug
- return "SuggestedWords:"
- + " mTypedWordValid=" + mTypedWordValid
- + " mWillAutoCorrect=" + mWillAutoCorrect
- + " mInputStyle=" + mInputStyle
- + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
- }
-
- public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
- final CompletionInfo[] infos) {
- final ArrayList<SuggestedWordInfo> result = new ArrayList<>();
- for (final CompletionInfo info : infos) {
- if (null == info || null == info.getText()) {
- continue;
- }
- result.add(new SuggestedWordInfo(info));
- }
- return result;
- }
-
- @Nonnull
- public static final SuggestedWords getEmptyInstance() {
- return SuggestedWords.EMPTY;
- }
-
- // Should get rid of the first one (what the user typed previously) from suggestions
- // and replace it with what the user currently typed.
- public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions(
- @Nonnull final SuggestedWordInfo typedWordInfo,
- @Nonnull final SuggestedWords previousSuggestions) {
- final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>();
- final HashSet<String> alreadySeen = new HashSet<>();
- suggestionsList.add(typedWordInfo);
- alreadySeen.add(typedWordInfo.mWord);
- final int previousSize = previousSuggestions.size();
- for (int index = 1; index < previousSize; index++) {
- final SuggestedWordInfo prevWordInfo = previousSuggestions.getInfo(index);
- final String prevWord = prevWordInfo.mWord;
- // Filter out duplicate suggestions.
- if (!alreadySeen.contains(prevWord)) {
- suggestionsList.add(prevWordInfo);
- alreadySeen.add(prevWord);
- }
- }
- return suggestionsList;
- }
-
- public SuggestedWordInfo getAutoCommitCandidate() {
- if (mSuggestedWordInfoList.size() <= 0) return null;
- final SuggestedWordInfo candidate = mSuggestedWordInfoList.get(0);
- return candidate.isEligibleForAutoCommit() ? candidate : null;
- }
-
- // non-final for testability.
- public static class SuggestedWordInfo {
- public static final int NOT_AN_INDEX = -1;
- public static final int NOT_A_CONFIDENCE = -1;
- public static final int MAX_SCORE = Integer.MAX_VALUE;
-
- private static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
- public static final int KIND_TYPED = 0; // What user typed
- public static final int KIND_CORRECTION = 1; // Simple correction/suggestion
- public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
- public static final int KIND_WHITELIST = 3; // Whitelisted word
- public static final int KIND_BLACKLIST = 4; // Blacklisted word
- public static final int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
- public static final int KIND_APP_DEFINED = 6; // Suggested by the application
- public static final int KIND_SHORTCUT = 7; // A shortcut
- public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
- // KIND_RESUMED: A resumed suggestion (comes from a span, currently this type is used only
- // in java for re-correction)
- public static final int KIND_RESUMED = 9;
- public static final int KIND_OOV_CORRECTION = 10; // Most probable string correction
-
- public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
- public static final int KIND_FLAG_EXACT_MATCH = 0x40000000;
- public static final int KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = 0x20000000;
- public static final int KIND_FLAG_APPROPRIATE_FOR_AUTO_CORRECTION = 0x10000000;
-
- public final String mWord;
- public final String mPrevWordsContext;
- // The completion info from the application. Null for suggestions that don't come from
- // the application (including keyboard-computed ones, so this is almost always null)
- public final CompletionInfo mApplicationSpecifiedCompletionInfo;
- public final int mScore;
- public final int mKindAndFlags;
- public final int mCodePointCount;
- @Deprecated
- public final Dictionary mSourceDict;
- // For auto-commit. This keeps track of the index inside the touch coordinates array
- // passed to native code to get suggestions for a gesture that corresponds to the first
- // letter of the second word.
- public final int mIndexOfTouchPointOfSecondWord;
- // For auto-commit. This is a measure of how confident we are that we can commit the
- // first word of this suggestion.
- public final int mAutoCommitFirstWordConfidence;
- private String mDebugString = "";
-
- /**
- * Create a new suggested word info.
- * @param word The string to suggest.
- * @param prevWordsContext previous words context.
- * @param score A measure of how likely this suggestion is.
- * @param kindAndFlags The kind of suggestion, as one of the above KIND_* constants with
- * flags.
- * @param sourceDict What instance of Dictionary produced this suggestion.
- * @param indexOfTouchPointOfSecondWord See mIndexOfTouchPointOfSecondWord.
- * @param autoCommitFirstWordConfidence See mAutoCommitFirstWordConfidence.
- */
- public SuggestedWordInfo(final String word, final String prevWordsContext,
- final int score, final int kindAndFlags,
- final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord,
- final int autoCommitFirstWordConfidence) {
- mWord = word;
- mPrevWordsContext = prevWordsContext;
- mApplicationSpecifiedCompletionInfo = null;
- mScore = score;
- mKindAndFlags = kindAndFlags;
- mSourceDict = sourceDict;
- mCodePointCount = StringUtils.codePointCount(mWord);
- mIndexOfTouchPointOfSecondWord = indexOfTouchPointOfSecondWord;
- mAutoCommitFirstWordConfidence = autoCommitFirstWordConfidence;
- }
-
- /**
- * Create a new suggested word info from an application-specified completion.
- * If the passed argument or its contained text is null, this throws a NPE.
- * @param applicationSpecifiedCompletion The application-specified completion info.
- */
- public SuggestedWordInfo(final CompletionInfo applicationSpecifiedCompletion) {
- mWord = applicationSpecifiedCompletion.getText().toString();
- mPrevWordsContext = "";
- mApplicationSpecifiedCompletionInfo = applicationSpecifiedCompletion;
- mScore = SuggestedWordInfo.MAX_SCORE;
- mKindAndFlags = SuggestedWordInfo.KIND_APP_DEFINED;
- mSourceDict = Dictionary.DICTIONARY_APPLICATION_DEFINED;
- mCodePointCount = StringUtils.codePointCount(mWord);
- mIndexOfTouchPointOfSecondWord = SuggestedWordInfo.NOT_AN_INDEX;
- mAutoCommitFirstWordConfidence = SuggestedWordInfo.NOT_A_CONFIDENCE;
- }
-
- public boolean isEligibleForAutoCommit() {
- return (isKindOf(KIND_CORRECTION) && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord);
- }
-
- public int getKind() {
- return (mKindAndFlags & KIND_MASK_KIND);
- }
-
- public boolean isKindOf(final int kind) {
- return getKind() == kind;
- }
-
- public boolean isPossiblyOffensive() {
- return (mKindAndFlags & KIND_FLAG_POSSIBLY_OFFENSIVE) != 0;
- }
-
- public boolean isExactMatch() {
- return (mKindAndFlags & KIND_FLAG_EXACT_MATCH) != 0;
- }
-
- public boolean isExactMatchWithIntentionalOmission() {
- return (mKindAndFlags & KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION) != 0;
- }
-
- public boolean isAprapreateForAutoCorrection() {
- return (mKindAndFlags & KIND_FLAG_APPROPRIATE_FOR_AUTO_CORRECTION) != 0;
- }
-
- public void setDebugString(final String str) {
- if (null == str) throw new NullPointerException("Debug info is null");
- mDebugString = str;
- }
-
- public String getDebugString() {
- return mDebugString;
- }
-
- public String getWord() {
- return mWord;
- }
-
- @Deprecated
- public Dictionary getSourceDictionary() {
- return mSourceDict;
- }
-
- public int codePointAt(int i) {
- return mWord.codePointAt(i);
- }
-
- @Override
- public String toString() {
- if (TextUtils.isEmpty(mDebugString)) {
- return mWord;
- }
- return mWord + " (" + mDebugString + ")";
- }
-
- /**
- * This will always remove the higher index if a duplicate is found.
- *
- * @return position of typed word in the candidate list
- */
- public static int removeDups(
- @Nullable final String typedWord,
- @Nonnull final ArrayList<SuggestedWordInfo> candidates) {
- if (candidates.isEmpty()) {
- return -1;
- }
- int firstOccurrenceOfWord = -1;
- if (!TextUtils.isEmpty(typedWord)) {
- firstOccurrenceOfWord = removeSuggestedWordInfoFromList(
- typedWord, candidates, -1 /* startIndexExclusive */);
- }
- for (int i = 0; i < candidates.size(); ++i) {
- removeSuggestedWordInfoFromList(
- candidates.get(i).mWord, candidates, i /* startIndexExclusive */);
- }
- return firstOccurrenceOfWord;
- }
-
- private static int removeSuggestedWordInfoFromList(
- @Nonnull final String word,
- @Nonnull final ArrayList<SuggestedWordInfo> candidates,
- final int startIndexExclusive) {
- int firstOccurrenceOfWord = -1;
- for (int i = startIndexExclusive + 1; i < candidates.size(); ++i) {
- final SuggestedWordInfo previous = candidates.get(i);
- if (word.equals(previous.mWord)) {
- if (firstOccurrenceOfWord == -1) {
- firstOccurrenceOfWord = i;
- }
- candidates.remove(i);
- --i;
- }
- }
- return firstOccurrenceOfWord;
- }
- }
-
- private static boolean isPrediction(final int inputStyle) {
- return INPUT_STYLE_PREDICTION == inputStyle
- || INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION == inputStyle;
- }
-
- public boolean isPrediction() {
- return isPrediction(mInputStyle);
- }
-
- /**
- * @return the {@link SuggestedWordInfo} which corresponds to the word that is originally
- * typed by the user. Otherwise returns {@code null}. Note that gesture input is not
- * considered to be a typed word.
- */
- @UsedForTesting
- public SuggestedWordInfo getTypedWordInfoOrNull() {
- if (SuggestedWords.INDEX_OF_TYPED_WORD >= size()) {
- return null;
- }
- final SuggestedWordInfo info = getInfo(SuggestedWords.INDEX_OF_TYPED_WORD);
- return (info.getKind() == SuggestedWordInfo.KIND_TYPED) ? info : null;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java
deleted file mode 100644
index 90221512f..000000000
--- a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.app.DownloadManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.os.Process;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
-import com.android.inputmethod.dictionarypack.DownloadManagerWrapper;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.setup.SetupActivity;
-import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils;
-
-/**
- * This class detects the {@link Intent#ACTION_MY_PACKAGE_REPLACED} broadcast intent when this IME
- * package has been replaced by a newer version of the same package. This class also detects
- * {@link Intent#ACTION_BOOT_COMPLETED} and {@link Intent#ACTION_USER_INITIALIZE} broadcast intent.
- *
- * If this IME has already been installed in the system image and a new version of this IME has
- * been installed, {@link Intent#ACTION_MY_PACKAGE_REPLACED} is received by this receiver and it
- * will hide the setup wizard's icon.
- *
- * If this IME has already been installed in the data partition and a new version of this IME has
- * been installed, {@link Intent#ACTION_MY_PACKAGE_REPLACED} is received by this receiver but it
- * will not hide the setup wizard's icon, and the icon will appear on the launcher.
- *
- * If this IME hasn't been installed yet and has been newly installed, no
- * {@link Intent#ACTION_MY_PACKAGE_REPLACED} will be sent and the setup wizard's icon will appear
- * on the launcher.
- *
- * When the device has been booted, {@link Intent#ACTION_BOOT_COMPLETED} is received by this
- * receiver and it checks whether the setup wizard's icon should be appeared or not on the launcher
- * depending on which partition this IME is installed.
- *
- * When the system locale has been changed, {@link Intent#ACTION_LOCALE_CHANGED} is received by
- * this receiver and the {@link KeyboardLayoutSet}'s cache is cleared.
- */
-public final class SystemBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = SystemBroadcastReceiver.class.getSimpleName();
-
- @Override
- public void onReceive(final Context context, final Intent intent) {
- final String intentAction = intent.getAction();
- if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) {
- Log.i(TAG, "Package has been replaced: " + context.getPackageName());
- // Need to restore additional subtypes because system always clears additional
- // subtypes when the package is replaced.
- RichInputMethodManager.init(context);
- final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
- final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes();
- richImm.setAdditionalInputMethodSubtypes(additionalSubtypes);
- toggleAppIcon(context);
-
- // Remove all the previously scheduled downloads. This will also makes sure
- // that any erroneously stuck downloads will get cleared. (b/21797386)
- removeOldDownloads(context);
- // b/21797386
- // downloadLatestDictionaries(context);
- } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) {
- Log.i(TAG, "Boot has been completed");
- toggleAppIcon(context);
- } else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) {
- Log.i(TAG, "System locale changed");
- KeyboardLayoutSet.onSystemLocaleChanged();
- }
-
- // The process that hosts this broadcast receiver is invoked and remains alive even after
- // 1) the package has been re-installed,
- // 2) the device has just booted,
- // 3) a new user has been created.
- // There is no good reason to keep the process alive if this IME isn't a current IME.
- final InputMethodManager imm = (InputMethodManager)
- context.getSystemService(Context.INPUT_METHOD_SERVICE);
- // Called to check whether this IME has been triggered by the current user or not
- final boolean isInputMethodManagerValidForUserOfThisProcess =
- !imm.getInputMethodList().isEmpty();
- final boolean isCurrentImeOfCurrentUser = isInputMethodManagerValidForUserOfThisProcess
- && UncachedInputMethodManagerUtils.isThisImeCurrent(context, imm);
- if (!isCurrentImeOfCurrentUser) {
- final int myPid = Process.myPid();
- Log.i(TAG, "Killing my process: pid=" + myPid);
- Process.killProcess(myPid);
- }
- }
-
- private void removeOldDownloads(Context context) {
- try {
- Log.i(TAG, "Removing the old downloads in progress of the previous keyboard version.");
- final DownloadManagerWrapper downloadManagerWrapper = new DownloadManagerWrapper(
- context);
- final DownloadManager.Query q = new DownloadManager.Query();
- // Query all the download statuses except the succeeded ones.
- q.setFilterByStatus(DownloadManager.STATUS_FAILED
- | DownloadManager.STATUS_PAUSED
- | DownloadManager.STATUS_PENDING
- | DownloadManager.STATUS_RUNNING);
- final Cursor c = downloadManagerWrapper.query(q);
- if (c != null) {
- for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
- final long downloadId = c
- .getLong(c.getColumnIndex(DownloadManager.COLUMN_ID));
- downloadManagerWrapper.remove(downloadId);
- Log.i(TAG, "Removed the download with Id: " + downloadId);
- }
- c.close();
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception while removing old downloads.");
- }
- }
-
- private void downloadLatestDictionaries(Context context) {
- final Intent updateIntent = new Intent(
- DictionaryPackConstants.INIT_AND_UPDATE_NOW_INTENT_ACTION);
- context.sendBroadcast(updateIntent);
- }
-
- public static void toggleAppIcon(final Context context) {
- final int appInfoFlags = context.getApplicationInfo().flags;
- final boolean isSystemApp = (appInfoFlags & ApplicationInfo.FLAG_SYSTEM) > 0;
- if (Log.isLoggable(TAG, Log.INFO)) {
- Log.i(TAG, "toggleAppIcon() : FLAG_SYSTEM = " + isSystemApp);
- }
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- context.getPackageManager().setComponentEnabledSetting(
- new ComponentName(context, SetupActivity.class),
- Settings.readShowSetupWizardIcon(prefs, context)
- ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
deleted file mode 100644
index fe24ccfc2..000000000
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
-import android.provider.UserDictionary.Words;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.annotation.Nullable;
-
-/**
- * An expandable dictionary that stores the words in the user dictionary provider into a binary
- * dictionary file to use it from native code.
- */
-public class UserBinaryDictionary extends ExpandableBinaryDictionary {
- private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName();
-
- // The user dictionary provider uses an empty string to mean "all languages".
- private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
- private static final int HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY = 250;
- private static final int LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY = 160;
-
- private static final String[] PROJECTION_QUERY = new String[] {Words.WORD, Words.FREQUENCY};
-
- private static final String NAME = "userunigram";
-
- private ContentObserver mObserver;
- final private String mLocaleString;
- final private boolean mAlsoUseMoreRestrictiveLocales;
-
- protected UserBinaryDictionary(final Context context, final Locale locale,
- final boolean alsoUseMoreRestrictiveLocales,
- final File dictFile, final String name) {
- super(context, getDictName(name, locale, dictFile), locale, Dictionary.TYPE_USER, dictFile);
- if (null == locale) throw new NullPointerException(); // Catch the error earlier
- final String localeStr = locale.toString();
- if (SubtypeLocaleUtils.NO_LANGUAGE.equals(localeStr)) {
- // If we don't have a locale, insert into the "all locales" user dictionary.
- mLocaleString = USER_DICTIONARY_ALL_LANGUAGES;
- } else {
- mLocaleString = localeStr;
- }
- mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
- ContentResolver cres = context.getContentResolver();
-
- mObserver = new ContentObserver(null) {
- @Override
- public void onChange(final boolean self) {
- // This hook is deprecated as of API level 16 (Build.VERSION_CODES.JELLY_BEAN),
- // but should still be supported for cases where the IME is running on an older
- // version of the platform.
- onChange(self, null);
- }
- // The following hook is only available as of API level 16
- // (Build.VERSION_CODES.JELLY_BEAN), and as such it will only work on JellyBean+
- // devices. On older versions of the platform, the hook above will be called instead.
- @Override
- public void onChange(final boolean self, final Uri uri) {
- setNeedsToRecreate();
- }
- };
- cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
- reloadDictionaryIfRequired();
- }
-
- // Note: This method is called by {@link DictionaryFacilitator} using Java reflection.
- @ExternallyReferenced
- public static UserBinaryDictionary getDictionary(
- final Context context, final Locale locale, final File dictFile,
- final String dictNamePrefix, @Nullable final String account) {
- return new UserBinaryDictionary(
- context, locale, false /* alsoUseMoreRestrictiveLocales */,
- dictFile, dictNamePrefix + NAME);
- }
-
- @Override
- public synchronized void close() {
- if (mObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- super.close();
- }
-
- @Override
- public void loadInitialContentsLocked() {
- // Split the locale. For example "en" => ["en"], "de_DE" => ["de", "DE"],
- // "en_US_foo_bar_qux" => ["en", "US", "foo_bar_qux"] because of the limit of 3.
- // This is correct for locale processing.
- // For this example, we'll look at the "en_US_POSIX" case.
- final String[] localeElements =
- TextUtils.isEmpty(mLocaleString) ? new String[] {} : mLocaleString.split("_", 3);
- final int length = localeElements.length;
-
- final StringBuilder request = new StringBuilder("(locale is NULL)");
- String localeSoFar = "";
- // At start, localeElements = ["en", "US", "POSIX"] ; localeSoFar = "" ;
- // and request = "(locale is NULL)"
- for (int i = 0; i < length; ++i) {
- // i | localeSoFar | localeElements
- // 0 | "" | ["en", "US", "POSIX"]
- // 1 | "en_" | ["en", "US", "POSIX"]
- // 2 | "en_US_" | ["en", "en_US", "POSIX"]
- localeElements[i] = localeSoFar + localeElements[i];
- localeSoFar = localeElements[i] + "_";
- // i | request
- // 0 | "(locale is NULL)"
- // 1 | "(locale is NULL) or (locale=?)"
- // 2 | "(locale is NULL) or (locale=?) or (locale=?)"
- request.append(" or (locale=?)");
- }
- // At the end, localeElements = ["en", "en_US", "en_US_POSIX"]; localeSoFar = en_US_POSIX_"
- // and request = "(locale is NULL) or (locale=?) or (locale=?) or (locale=?)"
-
- final String[] requestArguments;
- // If length == 3, we already have all the arguments we need (common prefix is meaningless
- // inside variants
- if (mAlsoUseMoreRestrictiveLocales && length < 3) {
- request.append(" or (locale like ?)");
- // The following creates an array with one more (null) position
- final String[] localeElementsWithMoreRestrictiveLocalesIncluded =
- Arrays.copyOf(localeElements, length + 1);
- localeElementsWithMoreRestrictiveLocalesIncluded[length] =
- localeElements[length - 1] + "_%";
- requestArguments = localeElementsWithMoreRestrictiveLocalesIncluded;
- // If for example localeElements = ["en"]
- // then requestArguments = ["en", "en_%"]
- // and request = (locale is NULL) or (locale=?) or (locale like ?)
- // If localeElements = ["en", "en_US"]
- // then requestArguments = ["en", "en_US", "en_US_%"]
- } else {
- requestArguments = localeElements;
- }
- final String requestString = request.toString();
- addWordsFromProjectionLocked(PROJECTION_QUERY, requestString, requestArguments);
- }
-
- private void addWordsFromProjectionLocked(final String[] query, String request,
- final String[] requestArguments)
- throws IllegalArgumentException {
- Cursor cursor = null;
- try {
- cursor = mContext.getContentResolver().query(
- Words.CONTENT_URI, query, request, requestArguments, null);
- addWordsLocked(cursor);
- } catch (final SQLiteException e) {
- Log.e(TAG, "SQLiteException in the remote User dictionary process.", e);
- } finally {
- try {
- if (null != cursor) cursor.close();
- } catch (final SQLiteException e) {
- Log.e(TAG, "SQLiteException in the remote User dictionary process.", e);
- }
- }
- }
-
- private static int scaleFrequencyFromDefaultToLatinIme(final int defaultFrequency) {
- // The default frequency for the user dictionary is 250 for historical reasons.
- // Latin IME considers a good value for the default user dictionary frequency
- // is about 160 considering the scale we use. So we are scaling down the values.
- if (defaultFrequency > Integer.MAX_VALUE / LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) {
- return (defaultFrequency / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY)
- * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY;
- }
- return (defaultFrequency * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY)
- / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY;
- }
-
- private void addWordsLocked(final Cursor cursor) {
- if (cursor == null) return;
- if (cursor.moveToFirst()) {
- final int indexWord = cursor.getColumnIndex(Words.WORD);
- final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY);
- while (!cursor.isAfterLast()) {
- final String word = cursor.getString(indexWord);
- final int frequency = cursor.getInt(indexFrequency);
- final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency);
- // Safeguard against adding really long words.
- if (word.length() <= MAX_WORD_LENGTH) {
- runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addUnigramLocked(word, adjustedFrequency, false /* isNotAWord */,
- false /* isPossiblyOffensive */,
- BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- }
- cursor.moveToNext();
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
deleted file mode 100644
index 8803edc88..000000000
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.event.CombinerChain;
-import com.android.inputmethod.event.Event;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.CoordinateUtils;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-import javax.annotation.Nonnull;
-
-/**
- * A place to store the currently composing word with information such as adjacent key codes as well
- */
-public final class WordComposer {
- private static final int MAX_WORD_LENGTH = DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH;
- private static final boolean DBG = DebugFlags.DEBUG_ENABLED;
-
- public static final int CAPS_MODE_OFF = 0;
- // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
- // aren't used anywhere in the code
- public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
- public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
- public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
- public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
-
- private CombinerChain mCombinerChain;
- private String mCombiningSpec; // Memory so that we don't uselessly recreate the combiner chain
-
- // The list of events that served to compose this string.
- private final ArrayList<Event> mEvents;
- private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
- private SuggestedWordInfo mAutoCorrection;
- private boolean mIsResumed;
- private boolean mIsBatchMode;
- // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user
- // gestures a word, is displeased with the results and hits backspace, then gestures again.
- // At the very least we should avoid re-suggesting the same thing, and to do that we memorize
- // the rejected suggestion in this variable.
- // TODO: this should be done in a comprehensive way by the User History feature instead of
- // as an ad-hockery here.
- private String mRejectedBatchModeSuggestion;
-
- // Cache these values for performance
- private CharSequence mTypedWordCache;
- private int mCapsCount;
- private int mDigitsCount;
- private int mCapitalizedMode;
- // This is the number of code points entered so far. This is not limited to MAX_WORD_LENGTH.
- // In general, this contains the size of mPrimaryKeyCodes, except when this is greater than
- // MAX_WORD_LENGTH in which case mPrimaryKeyCodes only contain the first MAX_WORD_LENGTH
- // code points.
- private int mCodePointSize;
- private int mCursorPositionWithinWord;
-
- /**
- * Whether the composing word has the only first char capitalized.
- */
- private boolean mIsOnlyFirstCharCapitalized;
-
- public WordComposer() {
- mCombinerChain = new CombinerChain("");
- mEvents = new ArrayList<>();
- mAutoCorrection = null;
- mIsResumed = false;
- mIsBatchMode = false;
- mCursorPositionWithinWord = 0;
- mRejectedBatchModeSuggestion = null;
- refreshTypedWordCache();
- }
-
- public ComposedData getComposedDataSnapshot() {
- return new ComposedData(getInputPointers(), isBatchMode(), mTypedWordCache.toString());
- }
-
- /**
- * Restart the combiners, possibly with a new spec.
- * @param combiningSpec The spec string for combining. This is found in the extra value.
- */
- public void restartCombining(final String combiningSpec) {
- final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
- if (!nonNullCombiningSpec.equals(mCombiningSpec)) {
- mCombinerChain = new CombinerChain(
- mCombinerChain.getComposingWordWithCombiningFeedback().toString());
- mCombiningSpec = nonNullCombiningSpec;
- }
- }
-
- /**
- * Clear out the keys registered so far.
- */
- public void reset() {
- mCombinerChain.reset();
- mEvents.clear();
- mAutoCorrection = null;
- mCapsCount = 0;
- mDigitsCount = 0;
- mIsOnlyFirstCharCapitalized = false;
- mIsResumed = false;
- mIsBatchMode = false;
- mCursorPositionWithinWord = 0;
- mRejectedBatchModeSuggestion = null;
- refreshTypedWordCache();
- }
-
- private final void refreshTypedWordCache() {
- mTypedWordCache = mCombinerChain.getComposingWordWithCombiningFeedback();
- mCodePointSize = Character.codePointCount(mTypedWordCache, 0, mTypedWordCache.length());
- }
-
- /**
- * Number of keystrokes in the composing word.
- * @return the number of keystrokes
- */
- public int size() {
- return mCodePointSize;
- }
-
- public boolean isSingleLetter() {
- return size() == 1;
- }
-
- public final boolean isComposingWord() {
- return size() > 0;
- }
-
- public InputPointers getInputPointers() {
- return mInputPointers;
- }
-
- /**
- * Process an event and return an event, and return a processed event to apply.
- * @param event the unprocessed event.
- * @return the processed event. Never null, but may be marked as consumed.
- */
- @Nonnull
- public Event processEvent(@Nonnull final Event event) {
- final Event processedEvent = mCombinerChain.processEvent(mEvents, event);
- // The retained state of the combiner chain may have changed while processing the event,
- // so we need to update our cache.
- refreshTypedWordCache();
- mEvents.add(event);
- return processedEvent;
- }
-
- /**
- * Apply a processed input event.
- *
- * All input events should be supported, including software/hardware events, characters as well
- * as deletions, multiple inputs and gestures.
- *
- * @param event the event to apply. Must not be null.
- */
- public void applyProcessedEvent(final Event event) {
- mCombinerChain.applyProcessedEvent(event);
- final int primaryCode = event.mCodePoint;
- final int keyX = event.mX;
- final int keyY = event.mY;
- final int newIndex = size();
- refreshTypedWordCache();
- mCursorPositionWithinWord = mCodePointSize;
- // We may have deleted the last one.
- if (0 == mCodePointSize) {
- mIsOnlyFirstCharCapitalized = false;
- }
- if (Constants.CODE_DELETE != event.mKeyCode) {
- if (newIndex < MAX_WORD_LENGTH) {
- // In the batch input mode, the {@code mInputPointers} holds batch input points and
- // shouldn't be overridden by the "typed key" coordinates
- // (See {@link #setBatchInputWord}).
- if (!mIsBatchMode) {
- // TODO: Set correct pointer id and time
- mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
- }
- }
- if (0 == newIndex) {
- mIsOnlyFirstCharCapitalized = Character.isUpperCase(primaryCode);
- } else {
- mIsOnlyFirstCharCapitalized = mIsOnlyFirstCharCapitalized
- && !Character.isUpperCase(primaryCode);
- }
- if (Character.isUpperCase(primaryCode)) mCapsCount++;
- if (Character.isDigit(primaryCode)) mDigitsCount++;
- }
- mAutoCorrection = null;
- }
-
- public void setCursorPositionWithinWord(final int posWithinWord) {
- mCursorPositionWithinWord = posWithinWord;
- // TODO: compute where that puts us inside the events
- }
-
- public boolean isCursorFrontOrMiddleOfComposingWord() {
- if (DBG && mCursorPositionWithinWord > mCodePointSize) {
- throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
- + "in a word of size " + mCodePointSize);
- }
- return mCursorPositionWithinWord != mCodePointSize;
- }
-
- /**
- * When the cursor is moved by the user, we need to update its position.
- * If it falls inside the currently composing word, we don't reset the composition, and
- * only update the cursor position.
- *
- * @param expectedMoveAmount How many java chars to move the cursor. Negative values move
- * the cursor backward, positive values move the cursor forward.
- * @return true if the cursor is still inside the composing word, false otherwise.
- */
- public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) {
- int actualMoveAmount = 0;
- int cursorPos = mCursorPositionWithinWord;
- // TODO: Don't make that copy. We can do this directly from mTypedWordCache.
- final int[] codePoints = StringUtils.toCodePointArray(mTypedWordCache);
- if (expectedMoveAmount >= 0) {
- // Moving the cursor forward for the expected amount or until the end of the word has
- // been reached, whichever comes first.
- while (actualMoveAmount < expectedMoveAmount && cursorPos < codePoints.length) {
- actualMoveAmount += Character.charCount(codePoints[cursorPos]);
- ++cursorPos;
- }
- } else {
- // Moving the cursor backward for the expected amount or until the start of the word
- // has been reached, whichever comes first.
- while (actualMoveAmount > expectedMoveAmount && cursorPos > 0) {
- --cursorPos;
- actualMoveAmount -= Character.charCount(codePoints[cursorPos]);
- }
- }
- // If the actual and expected amounts differ, we crossed the start or the end of the word
- // so the result would not be inside the composing word.
- if (actualMoveAmount != expectedMoveAmount) {
- return false;
- }
- mCursorPositionWithinWord = cursorPos;
- mCombinerChain.applyProcessedEvent(mCombinerChain.processEvent(
- mEvents, Event.createCursorMovedEvent(cursorPos)));
- return true;
- }
-
- public void setBatchInputPointers(final InputPointers batchPointers) {
- mInputPointers.set(batchPointers);
- mIsBatchMode = true;
- }
-
- public void setBatchInputWord(final String word) {
- reset();
- mIsBatchMode = true;
- final int length = word.length();
- for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
- final int codePoint = Character.codePointAt(word, i);
- // We don't want to override the batch input points that are held in mInputPointers
- // (See {@link #add(int,int,int)}).
- final Event processedEvent =
- processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
- applyProcessedEvent(processedEvent);
- }
- }
-
- /**
- * Set the currently composing word to the one passed as an argument.
- * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
- * @param codePoints the code points to set as the composing word.
- * @param coordinates the x, y coordinates of the key in the CoordinateUtils format
- */
- public void setComposingWord(final int[] codePoints, final int[] coordinates) {
- reset();
- final int length = codePoints.length;
- for (int i = 0; i < length; ++i) {
- final Event processedEvent =
- processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
- CoordinateUtils.xFromArray(coordinates, i),
- CoordinateUtils.yFromArray(coordinates, i)));
- applyProcessedEvent(processedEvent);
- }
- mIsResumed = true;
- }
-
- /**
- * Returns the word as it was typed, without any correction applied.
- * @return the word that was typed so far. Never returns null.
- */
- public String getTypedWord() {
- return mTypedWordCache.toString();
- }
-
- /**
- * Whether this composer is composing or about to compose a word in which only the first letter
- * is a capital.
- *
- * If we do have a composing word, we just return whether the word has indeed only its first
- * character capitalized. If we don't, then we return a value based on the capitalized mode,
- * which tell us what is likely to happen for the next composing word.
- *
- * @return capitalization preference
- */
- public boolean isOrWillBeOnlyFirstCharCapitalized() {
- return isComposingWord() ? mIsOnlyFirstCharCapitalized
- : (CAPS_MODE_OFF != mCapitalizedMode);
- }
-
- /**
- * Whether or not all of the user typed chars are upper case
- * @return true if all user typed chars are upper case, false otherwise
- */
- public boolean isAllUpperCase() {
- if (size() <= 1) {
- return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
- || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED;
- }
- return mCapsCount == size();
- }
-
- public boolean wasShiftedNoLock() {
- return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
- || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
- }
-
- /**
- * Returns true if more than one character is upper case, otherwise returns false.
- */
- public boolean isMostlyCaps() {
- return mCapsCount > 1;
- }
-
- /**
- * Returns true if we have digits in the composing word.
- */
- public boolean hasDigits() {
- return mDigitsCount > 0;
- }
-
- /**
- * Saves the caps mode at the start of composing.
- *
- * WordComposer needs to know about the caps mode for several reasons. The first is, we need
- * to know after the fact what the reason was, to register the correct form into the user
- * history dictionary: if the word was automatically capitalized, we should insert it in
- * all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
- * Also, batch input needs to know about the current caps mode to display correctly
- * capitalized suggestions.
- * @param mode the mode at the time of start
- */
- public void setCapitalizedModeAtStartComposingTime(final int mode) {
- mCapitalizedMode = mode;
- }
-
- /**
- * Before fetching suggestions, we don't necessarily know about the capitalized mode yet.
- *
- * If we don't have a composing word yet, we take a note of this mode so that we can then
- * supply this information to the suggestion process. If we have a composing word, then
- * the previous mode has priority over this.
- * @param mode the mode just before fetching suggestions
- */
- public void adviseCapitalizedModeBeforeFetchingSuggestions(final int mode) {
- if (!isComposingWord()) {
- mCapitalizedMode = mode;
- }
- }
-
- /**
- * Returns whether the word was automatically capitalized.
- * @return whether the word was automatically capitalized
- */
- public boolean wasAutoCapitalized() {
- return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
- || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
- }
-
- /**
- * Sets the auto-correction for this word.
- */
- public void setAutoCorrection(final SuggestedWordInfo autoCorrection) {
- mAutoCorrection = autoCorrection;
- }
-
- /**
- * @return the auto-correction for this word, or null if none.
- */
- public SuggestedWordInfo getAutoCorrectionOrNull() {
- return mAutoCorrection;
- }
-
- /**
- * @return whether we started composing this word by resuming suggestion on an existing string
- */
- public boolean isResumed() {
- return mIsResumed;
- }
-
- // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
- // committedWord should contain suggestion spans if applicable.
- public LastComposedWord commitWord(final int type, final CharSequence committedWord,
- final String separatorString, final NgramContext ngramContext) {
- // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
- // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
- // the last composed word to ensure this does not happen.
- final LastComposedWord lastComposedWord = new LastComposedWord(mEvents,
- mInputPointers, mTypedWordCache.toString(), committedWord, separatorString,
- ngramContext, mCapitalizedMode);
- mInputPointers.reset();
- if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
- && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
- lastComposedWord.deactivate();
- }
- mCapsCount = 0;
- mDigitsCount = 0;
- mIsBatchMode = false;
- mCombinerChain.reset();
- mEvents.clear();
- mCodePointSize = 0;
- mIsOnlyFirstCharCapitalized = false;
- mCapitalizedMode = CAPS_MODE_OFF;
- refreshTypedWordCache();
- mAutoCorrection = null;
- mCursorPositionWithinWord = 0;
- mIsResumed = false;
- mRejectedBatchModeSuggestion = null;
- return lastComposedWord;
- }
-
- public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
- mEvents.clear();
- Collections.copy(mEvents, lastComposedWord.mEvents);
- mInputPointers.set(lastComposedWord.mInputPointers);
- mCombinerChain.reset();
- refreshTypedWordCache();
- mCapitalizedMode = lastComposedWord.mCapitalizedMode;
- mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
- mCursorPositionWithinWord = mCodePointSize;
- mRejectedBatchModeSuggestion = null;
- mIsResumed = true;
- }
-
- public boolean isBatchMode() {
- return mIsBatchMode;
- }
-
- public void setRejectedBatchModeSuggestion(final String rejectedSuggestion) {
- mRejectedBatchModeSuggestion = rejectedSuggestion;
- }
-
- public String getRejectedBatchModeSuggestion() {
- return mRejectedBatchModeSuggestion;
- }
-
- @UsedForTesting
- void addInputPointerForTest(int index, int keyX, int keyY) {
- mInputPointers.addPointerAt(index, keyX, keyY, 0, 0);
- }
-
- @UsedForTesting
- void setTypedWordCacheForTests(String typedWordCacheForTests) {
- mTypedWordCache = typedWordCacheForTests;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/WordListInfo.java b/java/src/com/android/inputmethod/latin/WordListInfo.java
deleted file mode 100644
index 268fe9818..000000000
--- a/java/src/com/android/inputmethod/latin/WordListInfo.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-/**
- * Information container for a word list.
- */
-public final class WordListInfo {
- public final String mId;
- public final String mLocale;
- public final String mRawChecksum;
- public WordListInfo(final String id, final String locale, final String rawChecksum) {
- mId = id;
- mLocale = locale;
- mRawChecksum = rawChecksum;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/about/AboutPreferences.java b/java/src/com/android/inputmethod/latin/about/AboutPreferences.java
deleted file mode 100644
index ec7e6def5..000000000
--- a/java/src/com/android/inputmethod/latin/about/AboutPreferences.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.about;
-
-import android.app.Fragment;
-
-/**
- * Placeholer class of AboutPreferences. Never use this.
- */
-public final class AboutPreferences extends Fragment {
- private AboutPreferences() {
- // Prevents this from being instantiated
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java b/java/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java
deleted file mode 100644
index 3c39c9a16..000000000
--- a/java/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.accounts;
-
-import androidx.annotation.NonNull;
-
-import javax.annotation.Nullable;
-
-/**
- * Handles changes to account used to sign in to the keyboard.
- * e.g. account switching/sign-in/sign-out from the keyboard
- * user toggling the sync preference.
- */
-public class AccountStateChangedListener {
-
- /**
- * Called when the current account being used in keyboard is signed out.
- *
- * @param oldAccount the account that was signed out of.
- */
- public static void onAccountSignedOut(@NonNull String oldAccount) {
- }
-
- /**
- * Called when the user signs-in to the keyboard.
- * This may be called when the user switches accounts to sign in with a different account.
- *
- * @param oldAccount the previous account that was being used for sign-in.
- * May be null for a fresh sign-in.
- * @param newAccount the account being used for sign-in.
- */
- public static void onAccountSignedIn(@Nullable String oldAccount, @NonNull String newAccount) {
- }
-
- /**
- * Called when the user toggles the sync preference.
- *
- * @param account the account being used for sync.
- * @param syncEnabled indicates whether sync has been enabled or not.
- */
- public static void onSyncPreferenceChanged(@Nullable String account, boolean syncEnabled) {
- }
-
- /**
- * Forces an immediate sync to happen.
- * This should only be used for debugging purposes.
- *
- * @param account the account to use for sync.
- */
- public static void forceSync(@Nullable String account) {
- }
-
- /**
- * Forces an immediate deletion of user's data.
- * This should only be used for debugging purposes.
- *
- * @param account the account to use for sync.
- */
- public static void forceDelete(@Nullable String account) {
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java
deleted file mode 100644
index 00bcecf52..000000000
--- a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.accounts;
-
-import android.accounts.AccountManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.settings.LocalSettingsConstants;
-
-/**
- * {@link BroadcastReceiver} for {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION}.
- */
-public class AccountsChangedReceiver extends BroadcastReceiver {
- static final String TAG = "AccountsChangedReceiver";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(intent.getAction())) {
- Log.w(TAG, "Received unknown broadcast: " + intent);
- return;
- }
-
- // Ideally the account preference could live in a different preferences file
- // that wasn't being backed up and restored, however the preference fragments
- // currently only deal with the default shared preferences which is why
- // separating this out into a different file is not trivial currently.
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- final String currentAccount = prefs.getString(
- LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
- removeUnknownAccountFromPreference(prefs, getAccountsForLogin(context), currentAccount);
- }
-
- /**
- * Helper method to help test this receiver.
- */
- @UsedForTesting
- protected String[] getAccountsForLogin(Context context) {
- return LoginAccountUtils.getAccountsForLogin(context);
- }
-
- /**
- * Removes the currentAccount from preferences if it's not found
- * in the list of current accounts.
- */
- private static void removeUnknownAccountFromPreference(final SharedPreferences prefs,
- final String[] accounts, final String currentAccount) {
- if (currentAccount == null) {
- return;
- }
- for (final String account : accounts) {
- if (TextUtils.equals(currentAccount, account)) {
- return;
- }
- }
- Log.i(TAG, "The current account was removed from the system: " + currentAccount);
- prefs.edit()
- .remove(LocalSettingsConstants.PREF_ACCOUNT_NAME)
- .apply();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/accounts/AuthUtils.java b/java/src/com/android/inputmethod/latin/accounts/AuthUtils.java
deleted file mode 100644
index 31aba3631..000000000
--- a/java/src/com/android/inputmethod/latin/accounts/AuthUtils.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.accounts;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-
-import java.io.IOException;
-
-/**
- * Utility class that handles generation/invalidation of auth tokens in the app.
- */
-public class AuthUtils {
- private final AccountManager mAccountManager;
-
- public AuthUtils(Context context) {
- mAccountManager = AccountManager.get(context);
- }
-
- /**
- * @see AccountManager#invalidateAuthToken(String, String)
- */
- public void invalidateAuthToken(final String accountType, final String authToken) {
- mAccountManager.invalidateAuthToken(accountType, authToken);
- }
-
- /**
- * @see AccountManager#getAuthToken(
- * Account, String, Bundle, boolean, AccountManagerCallback, Handler)
- */
- public AccountManagerFuture<Bundle> getAuthToken(final Account account,
- final String authTokenType, final Bundle options, final boolean notifyAuthFailure,
- final AccountManagerCallback<Bundle> callback, final Handler handler) {
- return mAccountManager.getAuthToken(account, authTokenType, options, notifyAuthFailure,
- callback, handler);
- }
-
- /**
- * @see AccountManager#blockingGetAuthToken(Account, String, boolean)
- */
- public String blockingGetAuthToken(final Account account, final String authTokenType,
- final boolean notifyAuthFailure) throws OperationCanceledException,
- AuthenticatorException, IOException {
- return mAccountManager.blockingGetAuthToken(account, authTokenType, notifyAuthFailure);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java b/java/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java
deleted file mode 100644
index dcc64a223..000000000
--- a/java/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.accounts;
-
-import android.content.Context;
-
-import javax.annotation.Nonnull;
-
-/**
- * Utility class for retrieving accounts that may be used for login.
- */
-public class LoginAccountUtils {
- /**
- * This defines the type of account this class deals with.
- * This account type is used when listing the accounts available on the device for login.
- */
- public static final String ACCOUNT_TYPE = "";
-
- private LoginAccountUtils() {
- // This utility class is not publicly instantiable.
- }
-
- /**
- * Get the accounts available for login.
- *
- * @return an array of accounts. Empty (never null) if no accounts are available for login.
- */
- @Nonnull
- @SuppressWarnings("unused")
- public static String[] getAccountsForLogin(final Context context) {
- return new String[0];
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/define/DebugFlags.java b/java/src/com/android/inputmethod/latin/define/DebugFlags.java
deleted file mode 100644
index c509e8322..000000000
--- a/java/src/com/android/inputmethod/latin/define/DebugFlags.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.define;
-
-import android.content.SharedPreferences;
-
-public final class DebugFlags {
- public static final boolean DEBUG_ENABLED = false;
-
- private DebugFlags() {
- // This class is not publicly instantiable.
- }
-
- @SuppressWarnings("unused")
- public static void init(final SharedPreferences prefs) {
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/define/DecoderSpecificConstants.java b/java/src/com/android/inputmethod/latin/define/DecoderSpecificConstants.java
deleted file mode 100644
index 7f57ce858..000000000
--- a/java/src/com/android/inputmethod/latin/define/DecoderSpecificConstants.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.inputmethod.latin.define;
-
-/**
- * Decoder specific constants for LatinIme.
- */
-public class DecoderSpecificConstants {
-
- // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
- public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
-
- // (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported in Java side. Needs to modify
- // MAX_PREV_WORD_COUNT_FOR_N_GRAM in native/jni/src/defines.h for suggestions.
- public static final int MAX_PREV_WORD_COUNT_FOR_N_GRAM = 3;
-
- public static final String DECODER_DICT_SUFFIX = "";
-
- public static final boolean SHOULD_VERIFY_MAGIC_NUMBER = true;
- public static final boolean SHOULD_VERIFY_CHECKSUM = true;
- public static final boolean SHOULD_USE_DICT_VERSION = true;
- public static final boolean SHOULD_AUTO_CORRECT_USING_NON_WHITE_LISTED_SUGGESTION = false;
- public static final boolean SHOULD_REMOVE_PREVIOUSLY_REJECTED_SUGGESTION = true;
-}
diff --git a/java/src/com/android/inputmethod/latin/define/JniLibName.java b/java/src/com/android/inputmethod/latin/define/JniLibName.java
deleted file mode 100644
index abfc36d39..000000000
--- a/java/src/com/android/inputmethod/latin/define/JniLibName.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.define;
-
-public final class JniLibName {
- private JniLibName() {
- // This class is not publicly instantiable.
- }
-
- public static final String JNI_LIB_NAME = "jni_latinime";
-}
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java/src/com/android/inputmethod/latin/define/ProductionFlags.java
deleted file mode 100644
index f31c20822..000000000
--- a/java/src/com/android/inputmethod/latin/define/ProductionFlags.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.define;
-
-public final class ProductionFlags {
- private ProductionFlags() {
- // This class is not publicly instantiable.
- }
-
- public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false;
-
- /**
- * Include all suggestions from all dictionaries in
- * {@link com.android.inputmethod.latin.SuggestedWords#mRawSuggestions}.
- */
- public static final boolean INCLUDE_RAW_SUGGESTIONS = false;
-
- /**
- * When false, the metrics logging is not yet ready to be enabled.
- */
- public static final boolean IS_METRICS_LOGGING_SUPPORTED = false;
-
- /**
- * When {@code false}, the split keyboard is not yet ready to be enabled.
- */
- public static final boolean IS_SPLIT_KEYBOARD_SUPPORTED = true;
-
- /**
- * When {@code false}, account sign-in in keyboard is not yet ready to be enabled.
- */
- public static final boolean ENABLE_ACCOUNT_SIGN_IN = false;
-
- /**
- * When {@code true}, user history dictionary sync feature is ready to be enabled.
- */
- public static final boolean ENABLE_USER_HISTORY_DICTIONARY_SYNC =
- ENABLE_ACCOUNT_SIGN_IN && false;
-
- /**
- * When {@code true}, the IME maintains per account {@link UserHistoryDictionary}.
- */
- public static final boolean ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY =
- ENABLE_ACCOUNT_SIGN_IN && false;
-}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
deleted file mode 100644
index a7804a13f..000000000
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ /dev/null
@@ -1,2353 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.inputlogic;
-
-import android.graphics.Color;
-import android.os.SystemClock;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.style.BackgroundColorSpan;
-import android.text.style.SuggestionSpan;
-import android.util.Log;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.compat.SuggestionSpanUtils;
-import com.android.inputmethod.event.Event;
-import com.android.inputmethod.event.InputTransaction;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.DictionaryFacilitator;
-import com.android.inputmethod.latin.LastComposedWord;
-import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.RichInputConnection;
-import com.android.inputmethod.latin.Suggest;
-import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
-import com.android.inputmethod.latin.utils.AsyncResultHolder;
-import com.android.inputmethod.latin.utils.InputTypeUtils;
-import com.android.inputmethod.latin.utils.RecapitalizeStatus;
-import com.android.inputmethod.latin.utils.StatsUtils;
-import com.android.inputmethod.latin.utils.TextRange;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-
-/**
- * This class manages the input logic.
- */
-public final class InputLogic {
- private static final String TAG = InputLogic.class.getSimpleName();
-
- // TODO : Remove this member when we can.
- final LatinIME mLatinIME;
- private final SuggestionStripViewAccessor mSuggestionStripViewAccessor;
-
- // Never null.
- private InputLogicHandler mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
-
- // TODO : make all these fields private as soon as possible.
- // Current space state of the input method. This can be any of the above constants.
- private int mSpaceState;
- // Never null
- public SuggestedWords mSuggestedWords = SuggestedWords.getEmptyInstance();
- public final Suggest mSuggest;
- private final DictionaryFacilitator mDictionaryFacilitator;
-
- public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- // This has package visibility so it can be accessed from InputLogicHandler.
- /* package */ final WordComposer mWordComposer;
- public final RichInputConnection mConnection;
- private final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus();
-
- private int mDeleteCount;
- private long mLastKeyTime;
- public final TreeSet<Long> mCurrentlyPressedHardwareKeys = new TreeSet<>();
-
- // Keeps track of most recently inserted text (multi-character key) for reverting
- private String mEnteredText;
-
- // TODO: This boolean is persistent state and causes large side effects at unexpected times.
- // Find a way to remove it for readability.
- private boolean mIsAutoCorrectionIndicatorOn;
- private long mDoubleSpacePeriodCountdownStart;
-
- // The word being corrected while the cursor is in the middle of the word.
- // Note: This does not have a composing span, so it must be handled separately.
- private String mWordBeingCorrectedByCursor = null;
-
- /**
- * Create a new instance of the input logic.
- * @param latinIME the instance of the parent LatinIME. We should remove this when we can.
- * @param suggestionStripViewAccessor an object to access the suggestion strip view.
- * @param dictionaryFacilitator facilitator for getting suggestions and updating user history
- * dictionary.
- */
- public InputLogic(final LatinIME latinIME,
- final SuggestionStripViewAccessor suggestionStripViewAccessor,
- final DictionaryFacilitator dictionaryFacilitator) {
- mLatinIME = latinIME;
- mSuggestionStripViewAccessor = suggestionStripViewAccessor;
- mWordComposer = new WordComposer();
- mConnection = new RichInputConnection(latinIME);
- mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
- mSuggest = new Suggest(dictionaryFacilitator);
- mDictionaryFacilitator = dictionaryFacilitator;
- }
-
- /**
- * Initializes the input logic for input in an editor.
- *
- * Call this when input starts or restarts in some editor (typically, in onStartInputView).
- *
- * @param combiningSpec the combining spec string for this subtype
- * @param settingsValues the current settings values
- */
- public void startInput(final String combiningSpec, final SettingsValues settingsValues) {
- mEnteredText = null;
- mWordBeingCorrectedByCursor = null;
- mConnection.onStartInput();
- if (!mWordComposer.getTypedWord().isEmpty()) {
- // For messaging apps that offer send button, the IME does not get the opportunity
- // to capture the last word. This block should capture those uncommitted words.
- // The timestamp at which it is captured is not accurate but close enough.
- StatsUtils.onWordCommitUserTyped(
- mWordComposer.getTypedWord(), mWordComposer.isBatchMode());
- }
- mWordComposer.restartCombining(combiningSpec);
- resetComposingState(true /* alsoResetLastComposedWord */);
- mDeleteCount = 0;
- mSpaceState = SpaceState.NONE;
- mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once
- mCurrentlyPressedHardwareKeys.clear();
- mSuggestedWords = SuggestedWords.getEmptyInstance();
- // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying
- // so we try using some heuristics to find out about these and fix them.
- mConnection.tryFixLyingCursorPosition();
- cancelDoubleSpacePeriodCountdown();
- if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) {
- mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
- } else {
- mInputLogicHandler.reset();
- }
-
- if (settingsValues.mShouldShowLxxSuggestionUi) {
- mConnection.requestCursorUpdates(true /* enableMonitor */,
- true /* requestImmediateCallback */);
- }
- }
-
- /**
- * Call this when the subtype changes.
- * @param combiningSpec the spec string for the combining rules
- * @param settingsValues the current settings values
- */
- public void onSubtypeChanged(final String combiningSpec, final SettingsValues settingsValues) {
- finishInput();
- startInput(combiningSpec, settingsValues);
- }
-
- /**
- * Call this when the orientation changes.
- * @param settingsValues the current values of the settings.
- */
- public void onOrientationChange(final SettingsValues settingsValues) {
- // If !isComposingWord, #commitTyped() is a no-op, but still, it's better to avoid
- // the useless IPC of {begin,end}BatchEdit.
- if (mWordComposer.isComposingWord()) {
- mConnection.beginBatchEdit();
- // If we had a composition in progress, we need to commit the word so that the
- // suggestionsSpan will be added. This will allow resuming on the same suggestions
- // after rotation is finished.
- commitTyped(settingsValues, LastComposedWord.NOT_A_SEPARATOR);
- mConnection.endBatchEdit();
- }
- }
-
- /**
- * Clean up the input logic after input is finished.
- */
- public void finishInput() {
- if (mWordComposer.isComposingWord()) {
- mConnection.finishComposingText();
- StatsUtils.onWordCommitUserTyped(
- mWordComposer.getTypedWord(), mWordComposer.isBatchMode());
- }
- resetComposingState(true /* alsoResetLastComposedWord */);
- mInputLogicHandler.reset();
- }
-
- // Normally this class just gets out of scope after the process ends, but in unit tests, we
- // create several instances of LatinIME in the same process, which results in several
- // instances of InputLogic. This cleans up the associated handler so that tests don't leak
- // handlers.
- public void recycle() {
- final InputLogicHandler inputLogicHandler = mInputLogicHandler;
- mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
- inputLogicHandler.destroy();
- mDictionaryFacilitator.closeDictionaries();
- }
-
- /**
- * React to a string input.
- *
- * This is triggered by keys that input many characters at once, like the ".com" key or
- * some additional keys for example.
- *
- * @param settingsValues the current values of the settings.
- * @param event the input event containing the data.
- * @return the complete transaction object
- */
- public InputTransaction onTextInput(final SettingsValues settingsValues, final Event event,
- final int keyboardShiftMode, final LatinIME.UIHandler handler) {
- final String rawText = event.getTextToCommit().toString();
- final InputTransaction inputTransaction = new InputTransaction(settingsValues, event,
- SystemClock.uptimeMillis(), mSpaceState,
- getActualCapsMode(settingsValues, keyboardShiftMode));
- mConnection.beginBatchEdit();
- if (mWordComposer.isComposingWord()) {
- commitCurrentAutoCorrection(settingsValues, rawText, handler);
- } else {
- resetComposingState(true /* alsoResetLastComposedWord */);
- }
- handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_TYPING);
- final String text = performSpecificTldProcessingOnTextInput(rawText);
- if (SpaceState.PHANTOM == mSpaceState) {
- insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
- }
- mConnection.commitText(text, 1);
- StatsUtils.onWordCommitUserTyped(mEnteredText, mWordComposer.isBatchMode());
- mConnection.endBatchEdit();
- // Space state must be updated before calling updateShiftState
- mSpaceState = SpaceState.NONE;
- mEnteredText = text;
- mWordBeingCorrectedByCursor = null;
- inputTransaction.setDidAffectContents();
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- return inputTransaction;
- }
-
- /**
- * A suggestion was picked from the suggestion strip.
- * @param settingsValues the current values of the settings.
- * @param suggestionInfo the suggestion info.
- * @param keyboardShiftState the shift state of the keyboard, as returned by
- * {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()}
- * @return the complete transaction object
- */
- // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
- // interface
- public InputTransaction onPickSuggestionManually(final SettingsValues settingsValues,
- final SuggestedWordInfo suggestionInfo, final int keyboardShiftState,
- final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
- final SuggestedWords suggestedWords = mSuggestedWords;
- final String suggestion = suggestionInfo.mWord;
- // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
- if (suggestion.length() == 1 && suggestedWords.isPunctuationSuggestions()) {
- // We still want to log a suggestion click.
- StatsUtils.onPickSuggestionManually(
- mSuggestedWords, suggestionInfo, mDictionaryFacilitator);
- // Word separators are suggested before the user inputs something.
- // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
- final Event event = Event.createPunctuationSuggestionPickedEvent(suggestionInfo);
- return onCodeInput(settingsValues, event, keyboardShiftState,
- currentKeyboardScriptId, handler);
- }
-
- final Event event = Event.createSuggestionPickedEvent(suggestionInfo);
- final InputTransaction inputTransaction = new InputTransaction(settingsValues,
- event, SystemClock.uptimeMillis(), mSpaceState, keyboardShiftState);
- // Manual pick affects the contents of the editor, so we take note of this. It's important
- // for the sequence of language switching.
- inputTransaction.setDidAffectContents();
- mConnection.beginBatchEdit();
- if (SpaceState.PHANTOM == mSpaceState && suggestion.length() > 0
- // In the batch input mode, a manually picked suggested word should just replace
- // the current batch input text and there is no need for a phantom space.
- && !mWordComposer.isBatchMode()) {
- final int firstChar = Character.codePointAt(suggestion, 0);
- if (!settingsValues.isWordSeparator(firstChar)
- || settingsValues.isUsuallyPrecededBySpace(firstChar)) {
- insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
- }
- }
-
- // TODO: We should not need the following branch. We should be able to take the same
- // code path as for other kinds, use commitChosenWord, and do everything normally. We will
- // however need to reset the suggestion strip right away, because we know we can't take
- // the risk of calling commitCompletion twice because we don't know how the app will react.
- if (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_APP_DEFINED)) {
- mSuggestedWords = SuggestedWords.getEmptyInstance();
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- resetComposingState(true /* alsoResetLastComposedWord */);
- mConnection.commitCompletion(suggestionInfo.mApplicationSpecifiedCompletionInfo);
- mConnection.endBatchEdit();
- return inputTransaction;
- }
-
- commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
- LastComposedWord.NOT_A_SEPARATOR);
- mConnection.endBatchEdit();
- // Don't allow cancellation of manual pick
- mLastComposedWord.deactivate();
- // Space state must be updated before calling updateShiftState
- mSpaceState = SpaceState.PHANTOM;
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
-
- // If we're not showing the "Touch again to save", then update the suggestion strip.
- // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
- handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE);
-
- StatsUtils.onPickSuggestionManually(
- mSuggestedWords, suggestionInfo, mDictionaryFacilitator);
- StatsUtils.onWordCommitSuggestionPickedManually(
- suggestionInfo.mWord, mWordComposer.isBatchMode());
- return inputTransaction;
- }
-
- /**
- * Consider an update to the cursor position. Evaluate whether this update has happened as
- * part of normal typing or whether it was an explicit cursor move by the user. In any case,
- * do the necessary adjustments.
- * @param oldSelStart old selection start
- * @param oldSelEnd old selection end
- * @param newSelStart new selection start
- * @param newSelEnd new selection end
- * @param settingsValues the current values of the settings.
- * @return whether the cursor has moved as a result of user interaction.
- */
- public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd,
- final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) {
- if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) {
- return false;
- }
- // TODO: the following is probably better done in resetEntireInputState().
- // it should only happen when the cursor moved, and the very purpose of the
- // test below is to narrow down whether this happened or not. Likewise with
- // the call to updateShiftState.
- // We set this to NONE because after a cursor move, we don't want the space
- // state-related special processing to kick in.
- mSpaceState = SpaceState.NONE;
-
- final boolean selectionChangedOrSafeToReset =
- oldSelStart != newSelStart || oldSelEnd != newSelEnd // selection changed
- || !mWordComposer.isComposingWord(); // safe to reset
- final boolean hasOrHadSelection = (oldSelStart != oldSelEnd || newSelStart != newSelEnd);
- final int moveAmount = newSelStart - oldSelStart;
- // As an added small gift from the framework, it happens upon rotation when there
- // is a selection that we get a wrong cursor position delivered to startInput() that
- // does not get reflected in the oldSel{Start,End} parameters to the next call to
- // onUpdateSelection. In this case, we may have set a composition, and when we're here
- // we realize we shouldn't have. In theory, in this case, selectionChangedOrSafeToReset
- // should be true, but that is if the framework had taken that wrong cursor position
- // into account, which means we have to reset the entire composing state whenever there
- // is or was a selection regardless of whether it changed or not.
- if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions()
- || (selectionChangedOrSafeToReset
- && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
- // If we are composing a word and moving the cursor, we would want to set a
- // suggestion span for recorrection to work correctly. Unfortunately, that
- // would involve the keyboard committing some new text, which would move the
- // cursor back to where it was. Latin IME could then fix the position of the cursor
- // again, but the asynchronous nature of the calls results in this wreaking havoc
- // with selection on double tap and the like.
- // Another option would be to send suggestions each time we set the composing
- // text, but that is probably too expensive to do, so we decided to leave things
- // as is.
- // Also, we're posting a resume suggestions message, and this will update the
- // suggestions strip in a few milliseconds, so if we cleared the suggestion strip here
- // we'd have the suggestion strip noticeably janky. To avoid that, we don't clear
- // it here, which means we'll keep outdated suggestions for a split second but the
- // visual result is better.
- resetEntireInputState(newSelStart, newSelEnd, false /* clearSuggestionStrip */);
- // If the user is in the middle of correcting a word, we should learn it before moving
- // the cursor away.
- if (!TextUtils.isEmpty(mWordBeingCorrectedByCursor)) {
- final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
- System.currentTimeMillis());
- performAdditionToUserHistoryDictionary(settingsValues, mWordBeingCorrectedByCursor,
- NgramContext.EMPTY_PREV_WORDS_INFO);
- }
- } else {
- // resetEntireInputState calls resetCachesUponCursorMove, but forcing the
- // composition to end. But in all cases where we don't reset the entire input
- // state, we still want to tell the rich input connection about the new cursor
- // position so that it can update its caches.
- mConnection.resetCachesUponCursorMoveAndReturnSuccess(
- newSelStart, newSelEnd, false /* shouldFinishComposition */);
- }
-
- // The cursor has been moved : we now accept to perform recapitalization
- mRecapitalizeStatus.enable();
- // We moved the cursor. If we are touching a word, we need to resume suggestion.
- mLatinIME.mHandler.postResumeSuggestions(true /* shouldDelay */);
- // Stop the last recapitalization, if started.
- mRecapitalizeStatus.stop();
- mWordBeingCorrectedByCursor = null;
- return true;
- }
-
- /**
- * React to a code input. It may be a code point to insert, or a symbolic value that influences
- * the keyboard behavior.
- *
- * Typically, this is called whenever a key is pressed on the software keyboard. This is not
- * the entry point for gesture input; see the onBatchInput* family of functions for this.
- *
- * @param settingsValues the current settings values.
- * @param event the event to handle.
- * @param keyboardShiftMode the current shift mode of the keyboard, as returned by
- * {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()}
- * @return the complete transaction object
- */
- public InputTransaction onCodeInput(final SettingsValues settingsValues,
- @Nonnull final Event event, final int keyboardShiftMode,
- final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
- mWordBeingCorrectedByCursor = null;
- final Event processedEvent = mWordComposer.processEvent(event);
- final InputTransaction inputTransaction = new InputTransaction(settingsValues,
- processedEvent, SystemClock.uptimeMillis(), mSpaceState,
- getActualCapsMode(settingsValues, keyboardShiftMode));
- if (processedEvent.mKeyCode != Constants.CODE_DELETE
- || inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
- mDeleteCount = 0;
- }
- mLastKeyTime = inputTransaction.mTimestamp;
- mConnection.beginBatchEdit();
- if (!mWordComposer.isComposingWord()) {
- // TODO: is this useful? It doesn't look like it should be done here, but rather after
- // a word is committed.
- mIsAutoCorrectionIndicatorOn = false;
- }
-
- // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
- if (processedEvent.mCodePoint != Constants.CODE_SPACE) {
- cancelDoubleSpacePeriodCountdown();
- }
-
- Event currentEvent = processedEvent;
- while (null != currentEvent) {
- if (currentEvent.isConsumed()) {
- handleConsumedEvent(currentEvent, inputTransaction);
- } else if (currentEvent.isFunctionalKeyEvent()) {
- handleFunctionalEvent(currentEvent, inputTransaction, currentKeyboardScriptId,
- handler);
- } else {
- handleNonFunctionalEvent(currentEvent, inputTransaction, handler);
- }
- currentEvent = currentEvent.mNextEvent;
- }
- // Try to record the word being corrected when the user enters a word character or
- // the backspace key.
- if (!mConnection.hasSlowInputConnection() && !mWordComposer.isComposingWord()
- && (settingsValues.isWordCodePoint(processedEvent.mCodePoint) ||
- processedEvent.mKeyCode == Constants.CODE_DELETE)) {
- mWordBeingCorrectedByCursor = getWordAtCursor(
- settingsValues, currentKeyboardScriptId);
- }
- if (!inputTransaction.didAutoCorrect() && processedEvent.mKeyCode != Constants.CODE_SHIFT
- && processedEvent.mKeyCode != Constants.CODE_CAPSLOCK
- && processedEvent.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
- mLastComposedWord.deactivate();
- if (Constants.CODE_DELETE != processedEvent.mKeyCode) {
- mEnteredText = null;
- }
- mConnection.endBatchEdit();
- return inputTransaction;
- }
-
- public void onStartBatchInput(final SettingsValues settingsValues,
- final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) {
- mWordBeingCorrectedByCursor = null;
- mInputLogicHandler.onStartBatchInput();
- handler.showGesturePreviewAndSuggestionStrip(
- SuggestedWords.getEmptyInstance(), false /* dismissGestureFloatingPreviewText */);
- handler.cancelUpdateSuggestionStrip();
- ++mAutoCommitSequenceNumber;
- mConnection.beginBatchEdit();
- if (mWordComposer.isComposingWord()) {
- if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
- // If we are in the middle of a recorrection, we need to commit the recorrection
- // first so that we can insert the batch input at the current cursor position.
- // We also need to unlearn the original word that is now being corrected.
- unlearnWord(mWordComposer.getTypedWord(), settingsValues,
- Constants.EVENT_BACKSPACE);
- resetEntireInputState(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
- } else if (mWordComposer.isSingleLetter()) {
- // We auto-correct the previous (typed, not gestured) string iff it's one character
- // long. The reason for this is, even in the middle of gesture typing, you'll still
- // tap one-letter words and you want them auto-corrected (typically, "i" in English
- // should become "I"). However for any longer word, we assume that the reason for
- // tapping probably is that the word you intend to type is not in the dictionary,
- // so we do not attempt to correct, on the assumption that if that was a dictionary
- // word, the user would probably have gestured instead.
- commitCurrentAutoCorrection(settingsValues, LastComposedWord.NOT_A_SEPARATOR,
- handler);
- } else {
- commitTyped(settingsValues, LastComposedWord.NOT_A_SEPARATOR);
- }
- }
- final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
- if (Character.isLetterOrDigit(codePointBeforeCursor)
- || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
- final boolean autoShiftHasBeenOverriden = keyboardSwitcher.getKeyboardShiftMode() !=
- getCurrentAutoCapsState(settingsValues);
- mSpaceState = SpaceState.PHANTOM;
- if (!autoShiftHasBeenOverriden) {
- // When we change the space state, we need to update the shift state of the
- // keyboard unless it has been overridden manually. This is happening for example
- // after typing some letters and a period, then gesturing; the keyboard is not in
- // caps mode yet, but since a gesture is starting, it should go in caps mode,
- // unless the user explictly said it should not.
- keyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(settingsValues),
- getCurrentRecapitalizeState());
- }
- }
- mConnection.endBatchEdit();
- mWordComposer.setCapitalizedModeAtStartComposingTime(
- getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
- }
-
- /* The sequence number member is only used in onUpdateBatchInput. It is increased each time
- * auto-commit happens. The reason we need this is, when auto-commit happens we trim the
- * input pointers that are held in a singleton, and to know how much to trim we rely on the
- * results of the suggestion process that is held in mSuggestedWords.
- * However, the suggestion process is asynchronous, and sometimes we may enter the
- * onUpdateBatchInput method twice without having recomputed suggestions yet, or having
- * received new suggestions generated from not-yet-trimmed input pointers. In this case, the
- * mIndexOfTouchPointOfSecondWords member will be out of date, and we must not use it lest we
- * remove an unrelated number of pointers (possibly even more than are left in the input
- * pointers, leading to a crash).
- * To avoid that, we increase the sequence number each time we auto-commit and trim the
- * input pointers, and we do not use any suggested words that have been generated with an
- * earlier sequence number.
- */
- private int mAutoCommitSequenceNumber = 1;
- public void onUpdateBatchInput(final InputPointers batchPointers) {
- mInputLogicHandler.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber);
- }
-
- public void onEndBatchInput(final InputPointers batchPointers) {
- mInputLogicHandler.updateTailBatchInput(batchPointers, mAutoCommitSequenceNumber);
- ++mAutoCommitSequenceNumber;
- }
-
- public void onCancelBatchInput(final LatinIME.UIHandler handler) {
- mInputLogicHandler.onCancelBatchInput();
- handler.showGesturePreviewAndSuggestionStrip(
- SuggestedWords.getEmptyInstance(), true /* dismissGestureFloatingPreviewText */);
- }
-
- // TODO: on the long term, this method should become private, but it will be difficult.
- // Especially, how do we deal with InputMethodService.onDisplayCompletions?
- public void setSuggestedWords(final SuggestedWords suggestedWords) {
- if (!suggestedWords.isEmpty()) {
- final SuggestedWordInfo suggestedWordInfo;
- if (suggestedWords.mWillAutoCorrect) {
- suggestedWordInfo = suggestedWords.getInfo(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
- } else {
- // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
- // because it may differ from mWordComposer.mTypedWord.
- suggestedWordInfo = suggestedWords.mTypedWordInfo;
- }
- mWordComposer.setAutoCorrection(suggestedWordInfo);
- }
- mSuggestedWords = suggestedWords;
- final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect;
-
- // Put a blue underline to a word in TextView which will be auto-corrected.
- if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
- && mWordComposer.isComposingWord()) {
- mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
- final CharSequence textWithUnderline =
- getTextWithUnderline(mWordComposer.getTypedWord());
- // TODO: when called from an updateSuggestionStrip() call that results from a posted
- // message, this is called outside any batch edit. Potentially, this may result in some
- // janky flickering of the screen, although the display speed makes it unlikely in
- // the practice.
- setComposingTextInternal(textWithUnderline, 1);
- }
- }
-
- /**
- * Handle a consumed event.
- *
- * Consumed events represent events that have already been consumed, typically by the
- * combining chain.
- *
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- */
- private void handleConsumedEvent(final Event event, final InputTransaction inputTransaction) {
- // A consumed event may have text to commit and an update to the composing state, so
- // we evaluate both. With some combiners, it's possible than an event contains both
- // and we enter both of the following if clauses.
- final CharSequence textToCommit = event.getTextToCommit();
- if (!TextUtils.isEmpty(textToCommit)) {
- mConnection.commitText(textToCommit, 1);
- inputTransaction.setDidAffectContents();
- }
- if (mWordComposer.isComposingWord()) {
- setComposingTextInternal(mWordComposer.getTypedWord(), 1);
- inputTransaction.setDidAffectContents();
- inputTransaction.setRequiresUpdateSuggestions();
- }
- }
-
- /**
- * Handle a functional key event.
- *
- * A functional event is a special key, like delete, shift, emoji, or the settings key.
- * Non-special keys are those that generate a single code point.
- * This includes all letters, digits, punctuation, separators, emoji. It excludes keys that
- * manage keyboard-related stuff like shift, language switch, settings, layout switch, or
- * any key that results in multiple code points like the ".com" key.
- *
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- */
- private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
- final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
- switch (event.mKeyCode) {
- case Constants.CODE_DELETE:
- handleBackspaceEvent(event, inputTransaction, currentKeyboardScriptId);
- // Backspace is a functional key, but it affects the contents of the editor.
- inputTransaction.setDidAffectContents();
- break;
- case Constants.CODE_SHIFT:
- performRecapitalization(inputTransaction.mSettingsValues);
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- if (mSuggestedWords.isPrediction()) {
- inputTransaction.setRequiresUpdateSuggestions();
- }
- break;
- case Constants.CODE_CAPSLOCK:
- // Note: Changing keyboard to shift lock state is handled in
- // {@link KeyboardSwitcher#onEvent(Event)}.
- break;
- case Constants.CODE_SYMBOL_SHIFT:
- // Note: Calling back to the keyboard on the symbol Shift key is handled in
- // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
- break;
- case Constants.CODE_SWITCH_ALPHA_SYMBOL:
- // Note: Calling back to the keyboard on symbol key is handled in
- // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
- break;
- case Constants.CODE_SETTINGS:
- onSettingsKeyPressed();
- break;
- case Constants.CODE_SHORTCUT:
- // We need to switch to the shortcut IME. This is handled by LatinIME since the
- // input logic has no business with IME switching.
- break;
- case Constants.CODE_ACTION_NEXT:
- performEditorAction(EditorInfo.IME_ACTION_NEXT);
- break;
- case Constants.CODE_ACTION_PREVIOUS:
- performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
- break;
- case Constants.CODE_LANGUAGE_SWITCH:
- handleLanguageSwitchKey();
- break;
- case Constants.CODE_EMOJI:
- // Note: Switching emoji keyboard is being handled in
- // {@link KeyboardState#onEvent(Event,int)}.
- break;
- case Constants.CODE_ALPHA_FROM_EMOJI:
- // Note: Switching back from Emoji keyboard to the main keyboard is being
- // handled in {@link KeyboardState#onEvent(Event,int)}.
- break;
- case Constants.CODE_SHIFT_ENTER:
- final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
- event.mKeyCode, event.mX, event.mY, event.isKeyRepeat());
- handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler);
- // Shift + Enter is treated as a functional key but it results in adding a new
- // line, so that does affect the contents of the editor.
- inputTransaction.setDidAffectContents();
- break;
- default:
- throw new RuntimeException("Unknown key code : " + event.mKeyCode);
- }
- }
-
- /**
- * Handle an event that is not a functional event.
- *
- * These events are generally events that cause input, but in some cases they may do other
- * things like trigger an editor action.
- *
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- */
- private void handleNonFunctionalEvent(final Event event,
- final InputTransaction inputTransaction,
- final LatinIME.UIHandler handler) {
- inputTransaction.setDidAffectContents();
- switch (event.mCodePoint) {
- case Constants.CODE_ENTER:
- final EditorInfo editorInfo = getCurrentInputEditorInfo();
- final int imeOptionsActionId =
- InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo);
- if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) {
- // Either we have an actionLabel and we should performEditorAction with
- // actionId regardless of its value.
- performEditorAction(editorInfo.actionId);
- } else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) {
- // We didn't have an actionLabel, but we had another action to execute.
- // EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast,
- // EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it
- // means there should be an action and the app didn't bother to set a specific
- // code for it - presumably it only handles one. It does not have to be treated
- // in any specific way: anything that is not IME_ACTION_NONE should be sent to
- // performEditorAction.
- performEditorAction(imeOptionsActionId);
- } else {
- // No action label, and the action from imeOptions is NONE: this is a regular
- // enter key that should input a carriage return.
- handleNonSpecialCharacterEvent(event, inputTransaction, handler);
- }
- break;
- default:
- handleNonSpecialCharacterEvent(event, inputTransaction, handler);
- break;
- }
- }
-
- /**
- * Handle inputting a code point to the editor.
- *
- * Non-special keys are those that generate a single code point.
- * This includes all letters, digits, punctuation, separators, emoji. It excludes keys that
- * manage keyboard-related stuff like shift, language switch, settings, layout switch, or
- * any key that results in multiple code points like the ".com" key.
- *
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- */
- private void handleNonSpecialCharacterEvent(final Event event,
- final InputTransaction inputTransaction,
- final LatinIME.UIHandler handler) {
- final int codePoint = event.mCodePoint;
- mSpaceState = SpaceState.NONE;
- if (inputTransaction.mSettingsValues.isWordSeparator(codePoint)
- || Character.getType(codePoint) == Character.OTHER_SYMBOL) {
- handleSeparatorEvent(event, inputTransaction, handler);
- } else {
- if (SpaceState.PHANTOM == inputTransaction.mSpaceState) {
- if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
- // If we are in the middle of a recorrection, we need to commit the recorrection
- // first so that we can insert the character at the current cursor position.
- // We also need to unlearn the original word that is now being corrected.
- unlearnWord(mWordComposer.getTypedWord(), inputTransaction.mSettingsValues,
- Constants.EVENT_BACKSPACE);
- resetEntireInputState(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
- } else {
- commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR);
- }
- }
- handleNonSeparatorEvent(event, inputTransaction.mSettingsValues, inputTransaction);
- }
- }
-
- /**
- * Handle a non-separator.
- * @param event The event to handle.
- * @param settingsValues The current settings values.
- * @param inputTransaction The transaction in progress.
- */
- private void handleNonSeparatorEvent(final Event event, final SettingsValues settingsValues,
- final InputTransaction inputTransaction) {
- final int codePoint = event.mCodePoint;
- // TODO: refactor this method to stop flipping isComposingWord around all the time, and
- // make it shorter (possibly cut into several pieces). Also factor
- // handleNonSpecialCharacterEvent which has the same name as other handle* methods but is
- // not the same.
- boolean isComposingWord = mWordComposer.isComposingWord();
-
- // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
- // See onStartBatchInput() to see how to do it.
- if (SpaceState.PHANTOM == inputTransaction.mSpaceState
- && !settingsValues.isWordConnector(codePoint)) {
- if (isComposingWord) {
- // Validity check
- throw new RuntimeException("Should not be composing here");
- }
- insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
- }
-
- if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
- // If we are in the middle of a recorrection, we need to commit the recorrection
- // first so that we can insert the character at the current cursor position.
- // We also need to unlearn the original word that is now being corrected.
- unlearnWord(mWordComposer.getTypedWord(), inputTransaction.mSettingsValues,
- Constants.EVENT_BACKSPACE);
- resetEntireInputState(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
- isComposingWord = false;
- }
- // We want to find out whether to start composing a new word with this character. If so,
- // we need to reset the composing state and switch isComposingWord. The order of the
- // tests is important for good performance.
- // We only start composing if we're not already composing.
- if (!isComposingWord
- // We only start composing if this is a word code point. Essentially that means it's a
- // a letter or a word connector.
- && settingsValues.isWordCodePoint(codePoint)
- // We never go into composing state if suggestions are not requested.
- && settingsValues.needsToLookupSuggestions() &&
- // In languages with spaces, we only start composing a word when we are not already
- // touching a word. In languages without spaces, the above conditions are sufficient.
- // NOTE: If the InputConnection is slow, we skip the text-after-cursor check since it
- // can incur a very expensive getTextAfterCursor() lookup, potentially making the
- // keyboard UI slow and non-responsive.
- // TODO: Cache the text after the cursor so we don't need to go to the InputConnection
- // each time. We are already doing this for getTextBeforeCursor().
- (!settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
- || !mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations,
- !mConnection.hasSlowInputConnection() /* checkTextAfter */))) {
- // Reset entirely the composing state anyway, then start composing a new word unless
- // the character is a word connector. The idea here is, word connectors are not
- // separators and they should be treated as normal characters, except in the first
- // position where they should not start composing a word.
- isComposingWord = !settingsValues.mSpacingAndPunctuations.isWordConnector(codePoint);
- // Here we don't need to reset the last composed word. It will be reset
- // when we commit this one, if we ever do; if on the other hand we backspace
- // it entirely and resume suggestions on the previous word, we'd like to still
- // have touch coordinates for it.
- resetComposingState(false /* alsoResetLastComposedWord */);
- }
- if (isComposingWord) {
- mWordComposer.applyProcessedEvent(event);
- // If it's the first letter, make note of auto-caps state
- if (mWordComposer.isSingleLetter()) {
- mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
- }
- setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
- } else {
- final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
- inputTransaction);
-
- if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) {
- mSpaceState = SpaceState.WEAK;
- } else {
- sendKeyCodePoint(settingsValues, codePoint);
- }
- }
- inputTransaction.setRequiresUpdateSuggestions();
- }
-
- /**
- * Handle input of a separator code point.
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- */
- private void handleSeparatorEvent(final Event event, final InputTransaction inputTransaction,
- final LatinIME.UIHandler handler) {
- final int codePoint = event.mCodePoint;
- final SettingsValues settingsValues = inputTransaction.mSettingsValues;
- final boolean wasComposingWord = mWordComposer.isComposingWord();
- // We avoid sending spaces in languages without spaces if we were composing.
- final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint
- && !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
- && wasComposingWord;
- if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
- // If we are in the middle of a recorrection, we need to commit the recorrection
- // first so that we can insert the separator at the current cursor position.
- // We also need to unlearn the original word that is now being corrected.
- unlearnWord(mWordComposer.getTypedWord(), inputTransaction.mSettingsValues,
- Constants.EVENT_BACKSPACE);
- resetEntireInputState(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
- }
- // isComposingWord() may have changed since we stored wasComposing
- if (mWordComposer.isComposingWord()) {
- if (settingsValues.mAutoCorrectionEnabledPerUserSettings) {
- final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
- : StringUtils.newSingleCodePointString(codePoint);
- commitCurrentAutoCorrection(settingsValues, separator, handler);
- inputTransaction.setDidAutoCorrect();
- } else {
- commitTyped(settingsValues,
- StringUtils.newSingleCodePointString(codePoint));
- }
- }
-
- final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
- inputTransaction);
-
- final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
- && mConnection.isInsideDoubleQuoteOrAfterDigit();
-
- final boolean needsPrecedingSpace;
- if (SpaceState.PHANTOM != inputTransaction.mSpaceState) {
- needsPrecedingSpace = false;
- } else if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
- // Double quotes behave like they are usually preceded by space iff we are
- // not inside a double quote or after a digit.
- needsPrecedingSpace = !isInsideDoubleQuoteOrAfterDigit;
- } else if (settingsValues.mSpacingAndPunctuations.isClusteringSymbol(codePoint)
- && settingsValues.mSpacingAndPunctuations.isClusteringSymbol(
- mConnection.getCodePointBeforeCursor())) {
- needsPrecedingSpace = false;
- } else {
- needsPrecedingSpace = settingsValues.isUsuallyPrecededBySpace(codePoint);
- }
-
- if (needsPrecedingSpace) {
- insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
- }
-
- if (tryPerformDoubleSpacePeriod(event, inputTransaction)) {
- mSpaceState = SpaceState.DOUBLE;
- inputTransaction.setRequiresUpdateSuggestions();
- StatsUtils.onDoubleSpacePeriod();
- } else if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) {
- mSpaceState = SpaceState.SWAP_PUNCTUATION;
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- } else if (Constants.CODE_SPACE == codePoint) {
- if (!mSuggestedWords.isPunctuationSuggestions()) {
- mSpaceState = SpaceState.WEAK;
- }
-
- startDoubleSpacePeriodCountdown(inputTransaction);
- if (wasComposingWord || mSuggestedWords.isEmpty()) {
- inputTransaction.setRequiresUpdateSuggestions();
- }
-
- if (!shouldAvoidSendingCode) {
- sendKeyCodePoint(settingsValues, codePoint);
- }
- } else {
- if ((SpaceState.PHANTOM == inputTransaction.mSpaceState
- && settingsValues.isUsuallyFollowedBySpace(codePoint))
- || (Constants.CODE_DOUBLE_QUOTE == codePoint
- && isInsideDoubleQuoteOrAfterDigit)) {
- // If we are in phantom space state, and the user presses a separator, we want to
- // stay in phantom space state so that the next keypress has a chance to add the
- // space. For example, if I type "Good dat", pick "day" from the suggestion strip
- // then insert a comma and go on to typing the next word, I want the space to be
- // inserted automatically before the next word, the same way it is when I don't
- // input the comma. A double quote behaves like it's usually followed by space if
- // we're inside a double quote.
- // The case is a little different if the separator is a space stripper. Such a
- // separator does not normally need a space on the right (that's the difference
- // between swappers and strippers), so we should not stay in phantom space state if
- // the separator is a stripper. Hence the additional test above.
- mSpaceState = SpaceState.PHANTOM;
- }
-
- sendKeyCodePoint(settingsValues, codePoint);
-
- // Set punctuation right away. onUpdateSelection will fire but tests whether it is
- // already displayed or not, so it's okay.
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- }
-
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- }
-
- /**
- * Handle a press on the backspace key.
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- */
- private void handleBackspaceEvent(final Event event, final InputTransaction inputTransaction,
- final int currentKeyboardScriptId) {
- mSpaceState = SpaceState.NONE;
- mDeleteCount++;
-
- // In many cases after backspace, we need to update the shift state. Normally we need
- // to do this right away to avoid the shift state being out of date in case the user types
- // backspace then some other character very fast. However, in the case of backspace key
- // repeat, this can lead to flashiness when the cursor flies over positions where the
- // shift state should be updated, so if this is a key repeat, we update after a small delay.
- // Then again, even in the case of a key repeat, if the cursor is at start of text, it
- // can't go any further back, so we can update right away even if it's a key repeat.
- final int shiftUpdateKind =
- event.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0
- ? InputTransaction.SHIFT_UPDATE_LATER : InputTransaction.SHIFT_UPDATE_NOW;
- inputTransaction.requireShiftUpdate(shiftUpdateKind);
-
- if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
- // If we are in the middle of a recorrection, we need to commit the recorrection
- // first so that we can remove the character at the current cursor position.
- // We also need to unlearn the original word that is now being corrected.
- unlearnWord(mWordComposer.getTypedWord(), inputTransaction.mSettingsValues,
- Constants.EVENT_BACKSPACE);
- resetEntireInputState(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
- // When we exit this if-clause, mWordComposer.isComposingWord() will return false.
- }
- if (mWordComposer.isComposingWord()) {
- if (mWordComposer.isBatchMode()) {
- final String rejectedSuggestion = mWordComposer.getTypedWord();
- mWordComposer.reset();
- mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
- if (!TextUtils.isEmpty(rejectedSuggestion)) {
- unlearnWord(rejectedSuggestion, inputTransaction.mSettingsValues,
- Constants.EVENT_REJECTION);
- }
- StatsUtils.onBackspaceWordDelete(rejectedSuggestion.length());
- } else {
- mWordComposer.applyProcessedEvent(event);
- StatsUtils.onBackspacePressed(1);
- }
- if (mWordComposer.isComposingWord()) {
- setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
- } else {
- mConnection.commitText("", 1);
- }
- inputTransaction.setRequiresUpdateSuggestions();
- } else {
- if (mLastComposedWord.canRevertCommit()) {
- final String lastComposedWord = mLastComposedWord.mTypedWord;
- revertCommit(inputTransaction, inputTransaction.mSettingsValues);
- StatsUtils.onRevertAutoCorrect();
- StatsUtils.onWordCommitUserTyped(lastComposedWord, mWordComposer.isBatchMode());
- // Restart suggestions when backspacing into a reverted word. This is required for
- // the final corrected word to be learned, as learning only occurs when suggestions
- // are active.
- //
- // Note: restartSuggestionsOnWordTouchedByCursor is already called for normal
- // (non-revert) backspace handling.
- if (inputTransaction.mSettingsValues.isSuggestionsEnabledPerUserSettings()
- && inputTransaction.mSettingsValues.mSpacingAndPunctuations
- .mCurrentLanguageHasSpaces
- && !mConnection.isCursorFollowedByWordCharacter(
- inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
- restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
- false /* forStartInput */, currentKeyboardScriptId);
- }
- return;
- }
- if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
- // Cancel multi-character input: remove the text we just entered.
- // This is triggered on backspace after a key that inputs multiple characters,
- // like the smiley key or the .com key.
- mConnection.deleteTextBeforeCursor(mEnteredText.length());
- StatsUtils.onDeleteMultiCharInput(mEnteredText.length());
- mEnteredText = null;
- // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
- // In addition we know that spaceState is false, and that we should not be
- // reverting any autocorrect at this point. So we can safely return.
- return;
- }
- if (SpaceState.DOUBLE == inputTransaction.mSpaceState) {
- cancelDoubleSpacePeriodCountdown();
- if (mConnection.revertDoubleSpacePeriod(
- inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
- // No need to reset mSpaceState, it has already be done (that's why we
- // receive it as a parameter)
- inputTransaction.setRequiresUpdateSuggestions();
- mWordComposer.setCapitalizedModeAtStartComposingTime(
- WordComposer.CAPS_MODE_OFF);
- StatsUtils.onRevertDoubleSpacePeriod();
- return;
- }
- } else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
- if (mConnection.revertSwapPunctuation()) {
- StatsUtils.onRevertSwapPunctuation();
- // Likewise
- return;
- }
- }
-
- boolean hasUnlearnedWordBeingDeleted = false;
-
- // No cancelling of commit/double space/swap: we have a regular backspace.
- // We should backspace one char and restart suggestion if at the end of a word.
- if (mConnection.hasSelection()) {
- // If there is a selection, remove it.
- // We also need to unlearn the selected text.
- final CharSequence selection = mConnection.getSelectedText(0 /* 0 for no styles */);
- if (!TextUtils.isEmpty(selection)) {
- unlearnWord(selection.toString(), inputTransaction.mSettingsValues,
- Constants.EVENT_BACKSPACE);
- hasUnlearnedWordBeingDeleted = true;
- }
- final int numCharsDeleted = mConnection.getExpectedSelectionEnd()
- - mConnection.getExpectedSelectionStart();
- mConnection.setSelection(mConnection.getExpectedSelectionEnd(),
- mConnection.getExpectedSelectionEnd());
- mConnection.deleteTextBeforeCursor(numCharsDeleted);
- StatsUtils.onBackspaceSelectedText(numCharsDeleted);
- } else {
- // There is no selection, just delete one character.
- if (inputTransaction.mSettingsValues.isBeforeJellyBean()
- || inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()
- || Constants.NOT_A_CURSOR_POSITION
- == mConnection.getExpectedSelectionEnd()) {
- // There are three possible reasons to send a key event: either the field has
- // type TYPE_NULL, in which case the keyboard should send events, or we are
- // running in backward compatibility mode, or we don't know the cursor position.
- // Before Jelly bean, the keyboard would simulate a hardware keyboard event on
- // pressing enter or delete. This is bad for many reasons (there are race
- // conditions with commits) but some applications are relying on this behavior
- // so we continue to support it for older apps, so we retain this behavior if
- // the app has target SDK < JellyBean.
- // As for the case where we don't know the cursor position, it can happen
- // because of bugs in the framework. But the framework should know, so the next
- // best thing is to leave it to whatever it thinks is best.
- sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
- int totalDeletedLength = 1;
- if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
- // If this is an accelerated (i.e., double) deletion, then we need to
- // consider unlearning here because we may have already reached
- // the previous word, and will lose it after next deletion.
- hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
- inputTransaction.mSettingsValues, currentKeyboardScriptId);
- sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
- totalDeletedLength++;
- }
- StatsUtils.onBackspacePressed(totalDeletedLength);
- } else {
- final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
- if (codePointBeforeCursor == Constants.NOT_A_CODE) {
- // HACK for backward compatibility with broken apps that haven't realized
- // yet that hardware keyboards are not the only way of inputting text.
- // Nothing to delete before the cursor. We should not do anything, but many
- // broken apps expect something to happen in this case so that they can
- // catch it and have their broken interface react. If you need the keyboard
- // to do this, you're doing it wrong -- please fix your app.
- mConnection.deleteTextBeforeCursor(1);
- // TODO: Add a new StatsUtils method onBackspaceWhenNoText()
- return;
- }
- final int lengthToDelete =
- Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1;
- mConnection.deleteTextBeforeCursor(lengthToDelete);
- int totalDeletedLength = lengthToDelete;
- if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
- // If this is an accelerated (i.e., double) deletion, then we need to
- // consider unlearning here because we may have already reached
- // the previous word, and will lose it after next deletion.
- hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
- inputTransaction.mSettingsValues, currentKeyboardScriptId);
- final int codePointBeforeCursorToDeleteAgain =
- mConnection.getCodePointBeforeCursor();
- if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) {
- final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
- codePointBeforeCursorToDeleteAgain) ? 2 : 1;
- mConnection.deleteTextBeforeCursor(lengthToDeleteAgain);
- totalDeletedLength += lengthToDeleteAgain;
- }
- }
- StatsUtils.onBackspacePressed(totalDeletedLength);
- }
- }
- if (!hasUnlearnedWordBeingDeleted) {
- // Consider unlearning the word being deleted (if we have not done so already).
- unlearnWordBeingDeleted(
- inputTransaction.mSettingsValues, currentKeyboardScriptId);
- }
- if (mConnection.hasSlowInputConnection()) {
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- } else if (inputTransaction.mSettingsValues.isSuggestionsEnabledPerUserSettings()
- && inputTransaction.mSettingsValues.mSpacingAndPunctuations
- .mCurrentLanguageHasSpaces
- && !mConnection.isCursorFollowedByWordCharacter(
- inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
- restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
- false /* forStartInput */, currentKeyboardScriptId);
- }
- }
- }
-
- String getWordAtCursor(final SettingsValues settingsValues, final int currentKeyboardScriptId) {
- if (!mConnection.hasSelection()
- && settingsValues.isSuggestionsEnabledPerUserSettings()
- && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
- final TextRange range = mConnection.getWordRangeAtCursor(
- settingsValues.mSpacingAndPunctuations,
- currentKeyboardScriptId);
- if (range != null) {
- return range.mWord.toString();
- }
- }
- return "";
- }
-
- boolean unlearnWordBeingDeleted(
- final SettingsValues settingsValues, final int currentKeyboardScriptId) {
- if (mConnection.hasSlowInputConnection()) {
- // TODO: Refactor unlearning so that it does not incur any extra calls
- // to the InputConnection. That way it can still be performed on a slow
- // InputConnection.
- Log.w(TAG, "Skipping unlearning due to slow InputConnection.");
- return false;
- }
- // If we just started backspacing to delete a previous word (but have not
- // entered the composing state yet), unlearn the word.
- // TODO: Consider tracking whether or not this word was typed by the user.
- if (!mConnection.isCursorFollowedByWordCharacter(settingsValues.mSpacingAndPunctuations)) {
- final String wordBeingDeleted = getWordAtCursor(
- settingsValues, currentKeyboardScriptId);
- if (!TextUtils.isEmpty(wordBeingDeleted)) {
- unlearnWord(wordBeingDeleted, settingsValues, Constants.EVENT_BACKSPACE);
- return true;
- }
- }
- return false;
- }
-
- void unlearnWord(final String word, final SettingsValues settingsValues, final int eventType) {
- final NgramContext ngramContext = mConnection.getNgramContextFromNthPreviousWord(
- settingsValues.mSpacingAndPunctuations, 2);
- final long timeStampInSeconds = TimeUnit.MILLISECONDS.toSeconds(
- System.currentTimeMillis());
- mDictionaryFacilitator.unlearnFromUserHistory(
- word, ngramContext, timeStampInSeconds, eventType);
- }
-
- /**
- * Handle a press on the language switch key (the "globe key")
- */
- private void handleLanguageSwitchKey() {
- mLatinIME.switchToNextSubtype();
- }
-
- /**
- * Swap a space with a space-swapping punctuation sign.
- *
- * This method will check that there are two characters before the cursor and that the first
- * one is a space before it does the actual swapping.
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- * @return true if the swap has been performed, false if it was prevented by preliminary checks.
- */
- private boolean trySwapSwapperAndSpace(final Event event,
- final InputTransaction inputTransaction) {
- final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
- if (Constants.CODE_SPACE != codePointBeforeCursor) {
- return false;
- }
- mConnection.deleteTextBeforeCursor(1);
- final String text = event.getTextToCommit() + " ";
- mConnection.commitText(text, 1);
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- return true;
- }
-
- /*
- * Strip a trailing space if necessary and returns whether it's a swap weak space situation.
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- * @return whether we should swap the space instead of removing it.
- */
- private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead(final Event event,
- final InputTransaction inputTransaction) {
- final int codePoint = event.mCodePoint;
- final boolean isFromSuggestionStrip = event.isSuggestionStripPress();
- if (Constants.CODE_ENTER == codePoint &&
- SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
- mConnection.removeTrailingSpace();
- return false;
- }
- if ((SpaceState.WEAK == inputTransaction.mSpaceState
- || SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState)
- && isFromSuggestionStrip) {
- if (inputTransaction.mSettingsValues.isUsuallyPrecededBySpace(codePoint)) {
- return false;
- }
- if (inputTransaction.mSettingsValues.isUsuallyFollowedBySpace(codePoint)) {
- return true;
- }
- mConnection.removeTrailingSpace();
- }
- return false;
- }
-
- public void startDoubleSpacePeriodCountdown(final InputTransaction inputTransaction) {
- mDoubleSpacePeriodCountdownStart = inputTransaction.mTimestamp;
- }
-
- public void cancelDoubleSpacePeriodCountdown() {
- mDoubleSpacePeriodCountdownStart = 0;
- }
-
- public boolean isDoubleSpacePeriodCountdownActive(final InputTransaction inputTransaction) {
- return inputTransaction.mTimestamp - mDoubleSpacePeriodCountdownStart
- < inputTransaction.mSettingsValues.mDoubleSpacePeriodTimeout;
- }
-
- /**
- * Apply the double-space-to-period transformation if applicable.
- *
- * The double-space-to-period transformation means that we replace two spaces with a
- * period-space sequence of characters. This typically happens when the user presses space
- * twice in a row quickly.
- * This method will check that the double-space-to-period is active in settings, that the
- * two spaces have been input close enough together, that the typed character is a space
- * and that the previous character allows for the transformation to take place. If all of
- * these conditions are fulfilled, this method applies the transformation and returns true.
- * Otherwise, it does nothing and returns false.
- *
- * @param event The event to handle.
- * @param inputTransaction The transaction in progress.
- * @return true if we applied the double-space-to-period transformation, false otherwise.
- */
- private boolean tryPerformDoubleSpacePeriod(final Event event,
- final InputTransaction inputTransaction) {
- // Check the setting, the typed character and the countdown. If any of the conditions is
- // not fulfilled, return false.
- if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod
- || Constants.CODE_SPACE != event.mCodePoint
- || !isDoubleSpacePeriodCountdownActive(inputTransaction)) {
- return false;
- }
- // We only do this when we see one space and an accepted code point before the cursor.
- // The code point may be a surrogate pair but the space may not, so we need 3 chars.
- final CharSequence lastTwo = mConnection.getTextBeforeCursor(3, 0);
- if (null == lastTwo) return false;
- final int length = lastTwo.length();
- if (length < 2) return false;
- if (lastTwo.charAt(length - 1) != Constants.CODE_SPACE) {
- return false;
- }
- // We know there is a space in pos -1, and we have at least two chars. If we have only two
- // chars, isSurrogatePairs can't return true as charAt(1) is a space, so this is fine.
- final int firstCodePoint =
- Character.isSurrogatePair(lastTwo.charAt(0), lastTwo.charAt(1)) ?
- Character.codePointAt(lastTwo, length - 3) : lastTwo.charAt(length - 2);
- if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) {
- cancelDoubleSpacePeriodCountdown();
- mConnection.deleteTextBeforeCursor(1);
- final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations
- .mSentenceSeparatorAndSpace;
- mConnection.commitText(textToInsert, 1);
- inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- inputTransaction.setRequiresUpdateSuggestions();
- return true;
- }
- return false;
- }
-
- /**
- * Returns whether this code point can be followed by the double-space-to-period transformation.
- *
- * See #maybeDoubleSpaceToPeriod for details.
- * Generally, most word characters can be followed by the double-space-to-period transformation,
- * while most punctuation can't. Some punctuation however does allow for this to take place
- * after them, like the closing parenthesis for example.
- *
- * @param codePoint the code point after which we may want to apply the transformation
- * @return whether it's fine to apply the transformation after this code point.
- */
- private static boolean canBeFollowedByDoubleSpacePeriod(final int codePoint) {
- // TODO: This should probably be a denylist rather than a allowlist.
- // TODO: This should probably be language-dependant...
- return Character.isLetterOrDigit(codePoint)
- || codePoint == Constants.CODE_SINGLE_QUOTE
- || codePoint == Constants.CODE_DOUBLE_QUOTE
- || codePoint == Constants.CODE_CLOSING_PARENTHESIS
- || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
- || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
- || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET
- || codePoint == Constants.CODE_PLUS
- || codePoint == Constants.CODE_PERCENT
- || Character.getType(codePoint) == Character.OTHER_SYMBOL;
- }
-
- /**
- * Performs a recapitalization event.
- * @param settingsValues The current settings values.
- */
- private void performRecapitalization(final SettingsValues settingsValues) {
- if (!mConnection.hasSelection() || !mRecapitalizeStatus.mIsEnabled()) {
- return; // No selection or recapitalize is disabled for now
- }
- final int selectionStart = mConnection.getExpectedSelectionStart();
- final int selectionEnd = mConnection.getExpectedSelectionEnd();
- final int numCharsSelected = selectionEnd - selectionStart;
- if (numCharsSelected > Constants.MAX_CHARACTERS_FOR_RECAPITALIZATION) {
- // We bail out if we have too many characters for performance reasons. We don't want
- // to suck possibly multiple-megabyte data.
- return;
- }
- // If we have a recapitalize in progress, use it; otherwise, start a new one.
- if (!mRecapitalizeStatus.isStarted()
- || !mRecapitalizeStatus.isSetAt(selectionStart, selectionEnd)) {
- final CharSequence selectedText =
- mConnection.getSelectedText(0 /* flags, 0 for no styles */);
- if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection
- mRecapitalizeStatus.start(selectionStart, selectionEnd, selectedText.toString(),
- settingsValues.mLocale,
- settingsValues.mSpacingAndPunctuations.mSortedWordSeparators);
- // We trim leading and trailing whitespace.
- mRecapitalizeStatus.trim();
- }
- mConnection.finishComposingText();
- mRecapitalizeStatus.rotate();
- mConnection.setSelection(selectionEnd, selectionEnd);
- mConnection.deleteTextBeforeCursor(numCharsSelected);
- mConnection.commitText(mRecapitalizeStatus.getRecapitalizedString(), 0);
- mConnection.setSelection(mRecapitalizeStatus.getNewCursorStart(),
- mRecapitalizeStatus.getNewCursorEnd());
- }
-
- private void performAdditionToUserHistoryDictionary(final SettingsValues settingsValues,
- final String suggestion, @Nonnull final NgramContext ngramContext) {
- // If correction is not enabled, we don't add words to the user history dictionary.
- // That's to avoid unintended additions in some sensitive fields, or fields that
- // expect to receive non-words.
- if (!settingsValues.mAutoCorrectionEnabledPerUserSettings) return;
- if (mConnection.hasSlowInputConnection()) {
- // Since we don't unlearn when the user backspaces on a slow InputConnection,
- // turn off learning to guard against adding typos that the user later deletes.
- Log.w(TAG, "Skipping learning due to slow InputConnection.");
- return;
- }
-
- if (TextUtils.isEmpty(suggestion)) return;
- final boolean wasAutoCapitalized =
- mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps();
- final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
- System.currentTimeMillis());
- mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized,
- ngramContext, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive);
- }
-
- public void performUpdateSuggestionStripSync(final SettingsValues settingsValues,
- final int inputStyle) {
- long startTimeMillis = 0;
- if (DebugFlags.DEBUG_ENABLED) {
- startTimeMillis = System.currentTimeMillis();
- Log.d(TAG, "performUpdateSuggestionStripSync()");
- }
- // Check if we have a suggestion engine attached.
- if (!settingsValues.needsToLookupSuggestions()) {
- if (mWordComposer.isComposingWord()) {
- Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
- + "requested!");
- }
- // Clear the suggestions strip.
- mSuggestionStripViewAccessor.showSuggestionStrip(SuggestedWords.getEmptyInstance());
- return;
- }
-
- if (!mWordComposer.isComposingWord() && !settingsValues.mBigramPredictionEnabled) {
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- return;
- }
-
- final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<>("Suggest");
- mInputLogicHandler.getSuggestedWords(inputStyle, SuggestedWords.NOT_A_SEQUENCE_NUMBER,
- new OnGetSuggestedWordsCallback() {
- @Override
- public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
- final String typedWordString = mWordComposer.getTypedWord();
- final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(
- typedWordString, "" /* prevWordsContext */,
- SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE);
- // Show new suggestions if we have at least one. Otherwise keep the old
- // suggestions with the new typed word. Exception: if the length of the
- // typed word is <= 1 (after a deletion typically) we clear old suggestions.
- if (suggestedWords.size() > 1 || typedWordString.length() <= 1) {
- holder.set(suggestedWords);
- } else {
- holder.set(retrieveOlderSuggestions(typedWordInfo, mSuggestedWords));
- }
- }
- }
- );
-
- // This line may cause the current thread to wait.
- final SuggestedWords suggestedWords = holder.get(null,
- Constants.GET_SUGGESTED_WORDS_TIMEOUT);
- if (suggestedWords != null) {
- mSuggestionStripViewAccessor.showSuggestionStrip(suggestedWords);
- }
- if (DebugFlags.DEBUG_ENABLED) {
- long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
- Log.d(TAG, "performUpdateSuggestionStripSync() : " + runTimeMillis + " ms to finish");
- }
- }
-
- /**
- * Check if the cursor is touching a word. If so, restart suggestions on this word, else
- * do nothing.
- *
- * @param settingsValues the current values of the settings.
- * @param forStartInput whether we're doing this in answer to starting the input (as opposed
- * to a cursor move, for example). In ICS, there is a platform bug that we need to work
- * around only when we come here at input start time.
- */
- public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues,
- final boolean forStartInput,
- // TODO: remove this argument, put it into settingsValues
- final int currentKeyboardScriptId) {
- // HACK: We may want to special-case some apps that exhibit bad behavior in case of
- // recorrection. This is a temporary, stopgap measure that will be removed later.
- // TODO: remove this.
- if (settingsValues.isBrokenByRecorrection()
- // Recorrection is not supported in languages without spaces because we don't know
- // how to segment them yet.
- || !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
- // If no suggestions are requested, don't try restarting suggestions.
- || !settingsValues.needsToLookupSuggestions()
- // If we are currently in a batch input, we must not resume suggestions, or the result
- // of the batch input will replace the new composition. This may happen in the corner case
- // that the app moves the cursor on its own accord during a batch input.
- || mInputLogicHandler.isInBatchInput()
- // If the cursor is not touching a word, or if there is a selection, return right away.
- || mConnection.hasSelection()
- // If we don't know the cursor location, return.
- || mConnection.getExpectedSelectionStart() < 0) {
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- return;
- }
- final int expectedCursorPosition = mConnection.getExpectedSelectionStart();
- if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations,
- true /* checkTextAfter */)) {
- // Show predictions.
- mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF);
- mLatinIME.mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_RECORRECTION);
- return;
- }
- final TextRange range = mConnection.getWordRangeAtCursor(
- settingsValues.mSpacingAndPunctuations, currentKeyboardScriptId);
- if (null == range) return; // Happens if we don't have an input connection at all
- if (range.length() <= 0) {
- // Race condition, or touching a word in a non-supported script.
- mLatinIME.setNeutralSuggestionStrip();
- return;
- }
- // If for some strange reason (editor bug or so) we measure the text before the cursor as
- // longer than what the entire text is supposed to be, the safe thing to do is bail out.
- if (range.mHasUrlSpans) return; // If there are links, we don't resume suggestions. Making
- // edits to a linkified text through batch commands would ruin the URL spans, and unless
- // we take very complicated steps to preserve the whole link, we can't do things right so
- // we just do not resume because it's safer.
- final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor();
- if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
- final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
- final String typedWordString = range.mWord.toString();
- final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(typedWordString,
- "" /* prevWordsContext */, SuggestedWords.MAX_SUGGESTIONS + 1,
- SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
- suggestions.add(typedWordInfo);
- if (!isResumableWord(settingsValues, typedWordString)) {
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- return;
- }
- int i = 0;
- for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) {
- for (final String s : span.getSuggestions()) {
- ++i;
- if (!TextUtils.equals(s, typedWordString)) {
- suggestions.add(new SuggestedWordInfo(s,
- "" /* prevWordsContext */, SuggestedWords.MAX_SUGGESTIONS - i,
- SuggestedWordInfo.KIND_RESUMED, Dictionary.DICTIONARY_RESUMED,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE
- /* autoCommitFirstWordConfidence */));
- }
- }
- }
- final int[] codePoints = StringUtils.toCodePointArray(typedWordString);
- mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
- mWordComposer.setCursorPositionWithinWord(
- typedWordString.codePointCount(0, numberOfCharsInWordBeforeCursor));
- if (forStartInput) {
- mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
- }
- mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
- expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
- if (suggestions.size() <= 1) {
- // If there weren't any suggestion spans on this word, suggestions#size() will be 1
- // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
- // have no useful suggestions, so we will try to compute some for it instead.
- mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING,
- SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
- @Override
- public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
- doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords);
- }});
- } else {
- // We found suggestion spans in the word. We'll create the SuggestedWords out of
- // them, and make willAutoCorrect false. We make typedWordValid false, because the
- // color of the word in the suggestion strip changes according to this parameter,
- // and false gives the correct color.
- final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
- null /* rawSuggestions */, typedWordInfo, false /* typedWordValid */,
- false /* willAutoCorrect */, false /* isObsoleteSuggestions */,
- SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER);
- doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords);
- }
- }
-
- void doShowSuggestionsAndClearAutoCorrectionIndicator(final SuggestedWords suggestedWords) {
- mIsAutoCorrectionIndicatorOn = false;
- mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
- }
-
- /**
- * Reverts a previous commit with auto-correction.
- *
- * This is triggered upon pressing backspace just after a commit with auto-correction.
- *
- * @param inputTransaction The transaction in progress.
- * @param settingsValues the current values of the settings.
- */
- private void revertCommit(final InputTransaction inputTransaction,
- final SettingsValues settingsValues) {
- final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord;
- final String originallyTypedWordString =
- originallyTypedWord != null ? originallyTypedWord.toString() : "";
- final CharSequence committedWord = mLastComposedWord.mCommittedWord;
- final String committedWordString = committedWord.toString();
- final int cancelLength = committedWord.length();
- final String separatorString = mLastComposedWord.mSeparatorString;
- // If our separator is a space, we won't actually commit it,
- // but set the space state to PHANTOM so that a space will be inserted
- // on the next keypress
- final boolean usePhantomSpace = separatorString.equals(Constants.STRING_SPACE);
- // We want java chars, not codepoints for the following.
- final int separatorLength = separatorString.length();
- // TODO: should we check our saved separator against the actual contents of the text view?
- final int deleteLength = cancelLength + separatorLength;
- if (DebugFlags.DEBUG_ENABLED) {
- if (mWordComposer.isComposingWord()) {
- throw new RuntimeException("revertCommit, but we are composing a word");
- }
- final CharSequence wordBeforeCursor =
- mConnection.getTextBeforeCursor(deleteLength, 0).subSequence(0, cancelLength);
- if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
- throw new RuntimeException("revertCommit check failed: we thought we were "
- + "reverting \"" + committedWord
- + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
- }
- }
- mConnection.deleteTextBeforeCursor(deleteLength);
- if (!TextUtils.isEmpty(committedWord)) {
- unlearnWord(committedWordString, inputTransaction.mSettingsValues,
- Constants.EVENT_REVERT);
- }
- final String stringToCommit = originallyTypedWord +
- (usePhantomSpace ? "" : separatorString);
- final SpannableString textToCommit = new SpannableString(stringToCommit);
- if (committedWord instanceof SpannableString) {
- final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord;
- final Object[] spans = committedWordWithSuggestionSpans.getSpans(0,
- committedWord.length(), Object.class);
- final int lastCharIndex = textToCommit.length() - 1;
- // We will collect all suggestions in the following array.
- final ArrayList<String> suggestions = new ArrayList<>();
- // First, add the committed word to the list of suggestions.
- suggestions.add(committedWordString);
- for (final Object span : spans) {
- // If this is a suggestion span, we check that the word is not the committed word.
- // That should mostly be the case.
- // Given this, we add it to the list of suggestions, otherwise we discard it.
- if (span instanceof SuggestionSpan) {
- final SuggestionSpan suggestionSpan = (SuggestionSpan)span;
- for (final String suggestion : suggestionSpan.getSuggestions()) {
- if (!suggestion.equals(committedWordString)) {
- suggestions.add(suggestion);
- }
- }
- } else {
- // If this is not a suggestion span, we just add it as is.
- textToCommit.setSpan(span, 0 /* start */, lastCharIndex /* end */,
- committedWordWithSuggestionSpans.getSpanFlags(span));
- }
- }
- // Add the suggestion list to the list of suggestions.
- textToCommit.setSpan(new SuggestionSpan(mLatinIME /* context */,
- inputTransaction.mSettingsValues.mLocale,
- suggestions.toArray(new String[suggestions.size()]), 0 /* flags */,
- null /* notificationTargetClass */),
- 0 /* start */, lastCharIndex /* end */, 0 /* flags */);
- }
-
- if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
- mConnection.commitText(textToCommit, 1);
- if (usePhantomSpace) {
- mSpaceState = SpaceState.PHANTOM;
- }
- } else {
- // For languages without spaces, we revert the typed string but the cursor is flush
- // with the typed word, so we need to resume suggestions right away.
- final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
- mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
- setComposingTextInternal(textToCommit, 1);
- }
- // Don't restart suggestion yet. We'll restart if the user deletes the separator.
- mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
-
- // We have a separator between the word and the cursor: we should show predictions.
- inputTransaction.setRequiresUpdateSuggestions();
- }
-
- /**
- * Factor in auto-caps and manual caps and compute the current caps mode.
- * @param settingsValues the current settings values.
- * @param keyboardShiftMode the current shift mode of the keyboard. See
- * KeyboardSwitcher#getKeyboardShiftMode() for possible values.
- * @return the actual caps mode the keyboard is in right now.
- */
- private int getActualCapsMode(final SettingsValues settingsValues,
- final int keyboardShiftMode) {
- if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) {
- return keyboardShiftMode;
- }
- final int auto = getCurrentAutoCapsState(settingsValues);
- if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
- return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
- }
- if (0 != auto) {
- return WordComposer.CAPS_MODE_AUTO_SHIFTED;
- }
- return WordComposer.CAPS_MODE_OFF;
- }
-
- /**
- * Gets the current auto-caps state, factoring in the space state.
- *
- * This method tries its best to do this in the most efficient possible manner. It avoids
- * getting text from the editor if possible at all.
- * This is called from the KeyboardSwitcher (through a trampoline in LatinIME) because it
- * needs to know auto caps state to display the right layout.
- *
- * @param settingsValues the relevant settings values
- * @return a caps mode from TextUtils.CAP_MODE_* or Constants.TextUtils.CAP_MODE_OFF.
- */
- public int getCurrentAutoCapsState(final SettingsValues settingsValues) {
- if (!settingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
-
- final EditorInfo ei = getCurrentInputEditorInfo();
- if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
- final int inputType = ei.inputType;
- // Warning: this depends on mSpaceState, which may not be the most current value. If
- // mSpaceState gets updated later, whoever called this may need to be told about it.
- return mConnection.getCursorCapsMode(inputType, settingsValues.mSpacingAndPunctuations,
- SpaceState.PHANTOM == mSpaceState);
- }
-
- public int getCurrentRecapitalizeState() {
- if (!mRecapitalizeStatus.isStarted()
- || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd())) {
- // Not recapitalizing at the moment
- return RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
- }
- return mRecapitalizeStatus.getCurrentMode();
- }
-
- /**
- * @return the editor info for the current editor
- */
- private EditorInfo getCurrentInputEditorInfo() {
- return mLatinIME.getCurrentInputEditorInfo();
- }
-
- /**
- * Get n-gram context from the nth previous word before the cursor as context
- * for the suggestion process.
- * @param spacingAndPunctuations the current spacing and punctuations settings.
- * @param nthPreviousWord reverse index of the word to get (1-indexed)
- * @return the information of previous words
- */
- public NgramContext getNgramContextFromNthPreviousWordForSuggestion(
- final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) {
- if (spacingAndPunctuations.mCurrentLanguageHasSpaces) {
- // If we are typing in a language with spaces we can just look up the previous
- // word information from textview.
- return mConnection.getNgramContextFromNthPreviousWord(
- spacingAndPunctuations, nthPreviousWord);
- }
- if (LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord) {
- return NgramContext.BEGINNING_OF_SENTENCE;
- }
- return new NgramContext(new NgramContext.WordInfo(
- mLastComposedWord.mCommittedWord.toString()));
- }
-
- /**
- * Tests the passed word for resumability.
- *
- * We can resume suggestions on words whose first code point is a word code point (with some
- * nuances: check the code for details).
- *
- * @param settings the current values of the settings.
- * @param word the word to evaluate.
- * @return whether it's fine to resume suggestions on this word.
- */
- private static boolean isResumableWord(final SettingsValues settings, final String word) {
- final int firstCodePoint = word.codePointAt(0);
- return settings.isWordCodePoint(firstCodePoint)
- && Constants.CODE_SINGLE_QUOTE != firstCodePoint
- && Constants.CODE_DASH != firstCodePoint;
- }
-
- /**
- * @param actionId the action to perform
- */
- private void performEditorAction(final int actionId) {
- mConnection.performEditorAction(actionId);
- }
-
- /**
- * Perform the processing specific to inputting TLDs.
- *
- * Some keys input a TLD (specifically, the ".com" key) and this warrants some specific
- * processing. First, if this is a TLD, we ignore PHANTOM spaces -- this is done by type
- * of character in onCodeInput, but since this gets inputted as a whole string we need to
- * do it here specifically. Then, if the last character before the cursor is a period, then
- * we cut the dot at the start of ".com". This is because humans tend to type "www.google."
- * and then press the ".com" key and instinctively don't expect to get "www.google..com".
- *
- * @param text the raw text supplied to onTextInput
- * @return the text to actually send to the editor
- */
- private String performSpecificTldProcessingOnTextInput(final String text) {
- if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD
- || !Character.isLetter(text.charAt(1))) {
- // Not a tld: do nothing.
- return text;
- }
- // We have a TLD (or something that looks like this): make sure we don't add
- // a space even if currently in phantom mode.
- mSpaceState = SpaceState.NONE;
- final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
- // If no code point, #getCodePointBeforeCursor returns NOT_A_CODE_POINT.
- if (Constants.CODE_PERIOD == codePointBeforeCursor) {
- return text.substring(1);
- }
- return text;
- }
-
- /**
- * Handle a press on the settings key.
- */
- private void onSettingsKeyPressed() {
- mLatinIME.displaySettingsDialog();
- }
-
- /**
- * Resets the whole input state to the starting state.
- *
- * This will clear the composing word, reset the last composed word, clear the suggestion
- * strip and tell the input connection about it so that it can refresh its caches.
- *
- * @param newSelStart the new selection start, in java characters.
- * @param newSelEnd the new selection end, in java characters.
- * @param clearSuggestionStrip whether this method should clear the suggestion strip.
- */
- // TODO: how is this different from startInput ?!
- private void resetEntireInputState(final int newSelStart, final int newSelEnd,
- final boolean clearSuggestionStrip) {
- final boolean shouldFinishComposition = mWordComposer.isComposingWord();
- resetComposingState(true /* alsoResetLastComposedWord */);
- if (clearSuggestionStrip) {
- mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
- }
- mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
- shouldFinishComposition);
- }
-
- /**
- * Resets only the composing state.
- *
- * Compare #resetEntireInputState, which also clears the suggestion strip and resets the
- * input connection caches. This only deals with the composing state.
- *
- * @param alsoResetLastComposedWord whether to also reset the last composed word.
- */
- private void resetComposingState(final boolean alsoResetLastComposedWord) {
- mWordComposer.reset();
- if (alsoResetLastComposedWord) {
- mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- }
- }
-
- /**
- * Make a {@link com.android.inputmethod.latin.SuggestedWords} object containing a typed word
- * and obsolete suggestions.
- * See {@link com.android.inputmethod.latin.SuggestedWords#getTypedWordAndPreviousSuggestions(
- * SuggestedWordInfo, com.android.inputmethod.latin.SuggestedWords)}.
- * @param typedWordInfo The typed word as a SuggestedWordInfo.
- * @param previousSuggestedWords The previously suggested words.
- * @return Obsolete suggestions with the newly typed word.
- */
- static SuggestedWords retrieveOlderSuggestions(final SuggestedWordInfo typedWordInfo,
- final SuggestedWords previousSuggestedWords) {
- final SuggestedWords oldSuggestedWords = previousSuggestedWords.isPunctuationSuggestions()
- ? SuggestedWords.getEmptyInstance() : previousSuggestedWords;
- final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
- SuggestedWords.getTypedWordAndPreviousSuggestions(typedWordInfo, oldSuggestedWords);
- return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */,
- typedWordInfo, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */,
- true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle,
- SuggestedWords.NOT_A_SEQUENCE_NUMBER);
- }
-
- /**
- * @return the {@link Locale} of the {@link #mDictionaryFacilitator} if available. Otherwise
- * {@link Locale#ROOT}.
- */
- @Nonnull
- private Locale getDictionaryFacilitatorLocale() {
- return mDictionaryFacilitator != null ? mDictionaryFacilitator.getLocale() : Locale.ROOT;
- }
-
- /**
- * Gets a chunk of text with or the auto-correction indicator underline span as appropriate.
- *
- * This method looks at the old state of the auto-correction indicator to put or not put
- * the underline span as appropriate. It is important to note that this does not correspond
- * exactly to whether this word will be auto-corrected to or not: what's important here is
- * to keep the same indication as before.
- * When we add a new code point to a composing word, we don't know yet if we are going to
- * auto-correct it until the suggestions are computed. But in the mean time, we still need
- * to display the character and to extend the previous underline. To avoid any flickering,
- * the underline should keep the same color it used to have, even if that's not ultimately
- * the correct color for this new word. When the suggestions are finished evaluating, we
- * will call this method again to fix the color of the underline.
- *
- * @param text the text on which to maybe apply the span.
- * @return the same text, with the auto-correction underline span if that's appropriate.
- */
- // TODO: Shouldn't this go in some *Utils class instead?
- private CharSequence getTextWithUnderline(final String text) {
- // TODO: Locale should be determined based on context and the text given.
- return mIsAutoCorrectionIndicatorOn
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- mLatinIME, text, getDictionaryFacilitatorLocale())
- : text;
- }
-
- /**
- * Sends a DOWN key event followed by an UP key event to the editor.
- *
- * If possible at all, avoid using this method. It causes all sorts of race conditions with
- * the text view because it goes through a different, asynchronous binder. Also, batch edits
- * are ignored for key events. Use the normal software input methods instead.
- *
- * @param keyCode the key code to send inside the key event.
- */
- private void sendDownUpKeyEvent(final int keyCode) {
- final long eventTime = SystemClock.uptimeMillis();
- mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
- mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
- }
-
- /**
- * Sends a code point to the editor, using the most appropriate method.
- *
- * Normally we send code points with commitText, but there are some cases (where backward
- * compatibility is a concern for example) where we want to use deprecated methods.
- *
- * @param settingsValues the current values of the settings.
- * @param codePoint the code point to send.
- */
- // TODO: replace these two parameters with an InputTransaction
- private void sendKeyCodePoint(final SettingsValues settingsValues, final int codePoint) {
- // TODO: Remove this special handling of digit letters.
- // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
- if (codePoint >= '0' && codePoint <= '9') {
- sendDownUpKeyEvent(codePoint - '0' + KeyEvent.KEYCODE_0);
- return;
- }
-
- // TODO: we should do this also when the editor has TYPE_NULL
- if (Constants.CODE_ENTER == codePoint && settingsValues.isBeforeJellyBean()) {
- // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
- // a hardware keyboard event on pressing enter or delete. This is bad for many
- // reasons (there are race conditions with commits) but some applications are
- // relying on this behavior so we continue to support it for older apps.
- sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER);
- } else {
- mConnection.commitText(StringUtils.newSingleCodePointString(codePoint), 1);
- }
- }
-
- /**
- * Insert an automatic space, if the options allow it.
- *
- * This checks the options and the text before the cursor are appropriate before inserting
- * an automatic space.
- *
- * @param settingsValues the current values of the settings.
- */
- private void insertAutomaticSpaceIfOptionsAndTextAllow(final SettingsValues settingsValues) {
- if (settingsValues.shouldInsertSpacesAutomatically()
- && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
- && !mConnection.textBeforeCursorLooksLikeURL()) {
- sendKeyCodePoint(settingsValues, Constants.CODE_SPACE);
- }
- }
-
- /**
- * Do the final processing after a batch input has ended. This commits the word to the editor.
- * @param settingsValues the current values of the settings.
- * @param suggestedWords suggestedWords to use.
- */
- public void onUpdateTailBatchInputCompleted(final SettingsValues settingsValues,
- final SuggestedWords suggestedWords, final KeyboardSwitcher keyboardSwitcher) {
- final String batchInputText = suggestedWords.isEmpty() ? null : suggestedWords.getWord(0);
- if (TextUtils.isEmpty(batchInputText)) {
- return;
- }
- mConnection.beginBatchEdit();
- if (SpaceState.PHANTOM == mSpaceState) {
- insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
- }
- mWordComposer.setBatchInputWord(batchInputText);
- setComposingTextInternal(batchInputText, 1);
- mConnection.endBatchEdit();
- // Space state must be updated before calling updateShiftState
- mSpaceState = SpaceState.PHANTOM;
- keyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(settingsValues),
- getCurrentRecapitalizeState());
- }
-
- /**
- * Commit the typed string to the editor.
- *
- * This is typically called when we should commit the currently composing word without applying
- * auto-correction to it. Typically, we come here upon pressing a separator when the keyboard
- * is configured to not do auto-correction at all (because of the settings or the properties of
- * the editor). In this case, `separatorString' is set to the separator that was pressed.
- * We also come here in a variety of cases with external user action. For example, when the
- * cursor is moved while there is a composition, or when the keyboard is closed, or when the
- * user presses the Send button for an SMS, we don't auto-correct as that would be unexpected.
- * In this case, `separatorString' is set to NOT_A_SEPARATOR.
- *
- * @param settingsValues the current values of the settings.
- * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none.
- */
- public void commitTyped(final SettingsValues settingsValues, final String separatorString) {
- if (!mWordComposer.isComposingWord()) return;
- final String typedWord = mWordComposer.getTypedWord();
- if (typedWord.length() > 0) {
- final boolean isBatchMode = mWordComposer.isBatchMode();
- commitChosenWord(settingsValues, typedWord,
- LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString);
- StatsUtils.onWordCommitUserTyped(typedWord, isBatchMode);
- }
- }
-
- /**
- * Commit the current auto-correction.
- *
- * This will commit the best guess of the keyboard regarding what the user meant by typing
- * the currently composing word. The IME computes suggestions and assigns a confidence score
- * to each of them; when it's confident enough in one suggestion, it replaces the typed string
- * by this suggestion at commit time. When it's not confident enough, or when it has no
- * suggestions, or when the settings or environment does not allow for auto-correction, then
- * this method just commits the typed string.
- * Note that if suggestions are currently being computed in the background, this method will
- * block until the computation returns. This is necessary for consistency (it would be very
- * strange if pressing space would commit a different word depending on how fast you press).
- *
- * @param settingsValues the current value of the settings.
- * @param separator the separator that's causing the commit to happen.
- */
- private void commitCurrentAutoCorrection(final SettingsValues settingsValues,
- final String separator, final LatinIME.UIHandler handler) {
- // Complete any pending suggestions query first
- if (handler.hasPendingUpdateSuggestions()) {
- handler.cancelUpdateSuggestionStrip();
- // To know the input style here, we should retrieve the in-flight "update suggestions"
- // message and read its arg1 member here. However, the Handler class does not let
- // us retrieve this message, so we can't do that. But in fact, we notice that
- // we only ever come here when the input style was typing. In the case of batch
- // input, we update the suggestions synchronously when the tail batch comes. Likewise
- // for application-specified completions. As for recorrections, we never auto-correct,
- // so we don't come here either. Hence, the input style is necessarily
- // INPUT_STYLE_TYPING.
- performUpdateSuggestionStripSync(settingsValues, SuggestedWords.INPUT_STYLE_TYPING);
- }
- final SuggestedWordInfo autoCorrectionOrNull = mWordComposer.getAutoCorrectionOrNull();
- final String typedWord = mWordComposer.getTypedWord();
- final String stringToCommit = (autoCorrectionOrNull != null)
- ? autoCorrectionOrNull.mWord : typedWord;
- if (stringToCommit != null) {
- if (TextUtils.isEmpty(typedWord)) {
- throw new RuntimeException("We have an auto-correction but the typed word "
- + "is empty? Impossible! I must commit suicide.");
- }
- final boolean isBatchMode = mWordComposer.isBatchMode();
- commitChosenWord(settingsValues, stringToCommit,
- LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator);
- if (!typedWord.equals(stringToCommit)) {
- // This will make the correction flash for a short while as a visual clue
- // to the user that auto-correction happened. It has no other effect; in particular
- // note that this won't affect the text inside the text field AT ALL: it only makes
- // the segment of text starting at the supplied index and running for the length
- // of the auto-correction flash. At this moment, the "typedWord" argument is
- // ignored by TextView.
- mConnection.commitCorrection(new CorrectionInfo(
- mConnection.getExpectedSelectionEnd() - stringToCommit.length(),
- typedWord, stringToCommit));
- String prevWordsContext = (autoCorrectionOrNull != null)
- ? autoCorrectionOrNull.mPrevWordsContext
- : "";
- StatsUtils.onAutoCorrection(typedWord, stringToCommit, isBatchMode,
- mDictionaryFacilitator, prevWordsContext);
- StatsUtils.onWordCommitAutoCorrect(stringToCommit, isBatchMode);
- } else {
- StatsUtils.onWordCommitUserTyped(stringToCommit, isBatchMode);
- }
- }
- }
-
- /**
- * Commits the chosen word to the text field and saves it for later retrieval.
- *
- * @param settingsValues the current values of the settings.
- * @param chosenWord the word we want to commit.
- * @param commitType the type of the commit, as one of LastComposedWord.COMMIT_TYPE_*
- * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none.
- */
- private void commitChosenWord(final SettingsValues settingsValues, final String chosenWord,
- final int commitType, final String separatorString) {
- long startTimeMillis = 0;
- if (DebugFlags.DEBUG_ENABLED) {
- startTimeMillis = System.currentTimeMillis();
- Log.d(TAG, "commitChosenWord() : [" + chosenWord + "]");
- }
- final SuggestedWords suggestedWords = mSuggestedWords;
- // TODO: Locale should be determined based on context and the text given.
- final Locale locale = getDictionaryFacilitatorLocale();
- final CharSequence chosenWordWithSuggestions = chosenWord;
- // b/21926256
- // SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord,
- // suggestedWords, locale);
- if (DebugFlags.DEBUG_ENABLED) {
- long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
- Log.d(TAG, "commitChosenWord() : " + runTimeMillis + " ms to run "
- + "SuggestionSpanUtils.getTextWithSuggestionSpan()");
- startTimeMillis = System.currentTimeMillis();
- }
- // When we are composing word, get n-gram context from the 2nd previous word because the
- // 1st previous word is the word to be committed. Otherwise get n-gram context from the 1st
- // previous word.
- final NgramContext ngramContext = mConnection.getNgramContextFromNthPreviousWord(
- settingsValues.mSpacingAndPunctuations, mWordComposer.isComposingWord() ? 2 : 1);
- if (DebugFlags.DEBUG_ENABLED) {
- long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
- Log.d(TAG, "commitChosenWord() : " + runTimeMillis + " ms to run "
- + "Connection.getNgramContextFromNthPreviousWord()");
- Log.d(TAG, "commitChosenWord() : NgramContext = " + ngramContext);
- startTimeMillis = System.currentTimeMillis();
- }
- mConnection.commitText(chosenWordWithSuggestions, 1);
- if (DebugFlags.DEBUG_ENABLED) {
- long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
- Log.d(TAG, "commitChosenWord() : " + runTimeMillis + " ms to run "
- + "Connection.commitText");
- startTimeMillis = System.currentTimeMillis();
- }
- // Add the word to the user history dictionary
- performAdditionToUserHistoryDictionary(settingsValues, chosenWord, ngramContext);
- if (DebugFlags.DEBUG_ENABLED) {
- long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
- Log.d(TAG, "commitChosenWord() : " + runTimeMillis + " ms to run "
- + "performAdditionToUserHistoryDictionary()");
- startTimeMillis = System.currentTimeMillis();
- }
- // TODO: figure out here if this is an auto-correct or if the best word is actually
- // what user typed. Note: currently this is done much later in
- // LastComposedWord#didCommitTypedWord by string equality of the remembered
- // strings.
- mLastComposedWord = mWordComposer.commitWord(commitType,
- chosenWordWithSuggestions, separatorString, ngramContext);
- if (DebugFlags.DEBUG_ENABLED) {
- long runTimeMillis = System.currentTimeMillis() - startTimeMillis;
- Log.d(TAG, "commitChosenWord() : " + runTimeMillis + " ms to run "
- + "WordComposer.commitWord()");
- startTimeMillis = System.currentTimeMillis();
- }
- }
-
- /**
- * Retry resetting caches in the rich input connection.
- *
- * When the editor can't be accessed we can't reset the caches, so we schedule a retry.
- * This method handles the retry, and re-schedules a new retry if we still can't access.
- * We only retry up to 5 times before giving up.
- *
- * @param tryResumeSuggestions Whether we should resume suggestions or not.
- * @param remainingTries How many times we may try again before giving up.
- * @return whether true if the caches were successfully reset, false otherwise.
- */
- public boolean retryResetCachesAndReturnSuccess(final boolean tryResumeSuggestions,
- final int remainingTries, final LatinIME.UIHandler handler) {
- final boolean shouldFinishComposition = mConnection.hasSelection()
- || !mConnection.isCursorPositionKnown();
- if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(
- mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(),
- shouldFinishComposition)) {
- if (0 < remainingTries) {
- handler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
- return false;
- }
- // If remainingTries is 0, we should stop waiting for new tries, however we'll still
- // return true as we need to perform other tasks (for example, loading the keyboard).
- }
- mConnection.tryFixLyingCursorPosition();
- if (tryResumeSuggestions) {
- handler.postResumeSuggestions(true /* shouldDelay */);
- }
- return true;
- }
-
- public void getSuggestedWords(final SettingsValues settingsValues,
- final Keyboard keyboard, final int keyboardShiftMode, final int inputStyle,
- final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
- mWordComposer.adviseCapitalizedModeBeforeFetchingSuggestions(
- getActualCapsMode(settingsValues, keyboardShiftMode));
- mSuggest.getSuggestedWords(mWordComposer,
- getNgramContextFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations,
- // Get the word on which we should search the bigrams. If we are composing
- // a word, it's whatever is *before* the half-committed word in the buffer,
- // hence 2; if we aren't, we should just skip whitespace if any, so 1.
- mWordComposer.isComposingWord() ? 2 : 1),
- keyboard,
- new SettingsValuesForSuggestion(settingsValues.mBlockPotentiallyOffensive),
- settingsValues.mAutoCorrectionEnabledPerUserSettings,
- inputStyle, sequenceNumber, callback);
- }
-
- /**
- * Used as an injection point for each call of
- * {@link RichInputConnection#setComposingText(CharSequence, int)}.
- *
- * <p>Currently using this method is optional and you can still directly call
- * {@link RichInputConnection#setComposingText(CharSequence, int)}, but it is recommended to
- * use this method whenever possible.<p>
- * <p>TODO: Should we move this mechanism to {@link RichInputConnection}?</p>
- *
- * @param newComposingText the composing text to be set
- * @param newCursorPosition the new cursor position
- */
- private void setComposingTextInternal(final CharSequence newComposingText,
- final int newCursorPosition) {
- setComposingTextInternalWithBackgroundColor(newComposingText, newCursorPosition,
- Color.TRANSPARENT, newComposingText.length());
- }
-
- /**
- * Equivalent to {@link #setComposingTextInternal(CharSequence, int)} except that this method
- * allows to set {@link BackgroundColorSpan} to the composing text with the given color.
- *
- * <p>TODO: Currently the background color is exclusive with the black underline, which is
- * automatically added by the framework. We need to change the framework if we need to have both
- * of them at the same time.</p>
- * <p>TODO: Should we move this method to {@link RichInputConnection}?</p>
- *
- * @param newComposingText the composing text to be set
- * @param newCursorPosition the new cursor position
- * @param backgroundColor the background color to be set to the composing text. Set
- * {@link Color#TRANSPARENT} to disable the background color.
- * @param coloredTextLength the length of text, in Java chars, which should be rendered with
- * the given background color.
- */
- private void setComposingTextInternalWithBackgroundColor(final CharSequence newComposingText,
- final int newCursorPosition, final int backgroundColor, final int coloredTextLength) {
- final CharSequence composingTextToBeSet;
- if (backgroundColor == Color.TRANSPARENT) {
- composingTextToBeSet = newComposingText;
- } else {
- final SpannableString spannable = new SpannableString(newComposingText);
- final BackgroundColorSpan backgroundColorSpan =
- new BackgroundColorSpan(backgroundColor);
- final int spanLength = Math.min(coloredTextLength, spannable.length());
- spannable.setSpan(backgroundColorSpan, 0, spanLength,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
- composingTextToBeSet = spannable;
- }
- mConnection.setComposingText(composingTextToBeSet, newCursorPosition);
- }
-
- /**
- * Gets an object allowing private IME commands to be sent to the
- * underlying editor.
- * @return An object for sending private commands to the underlying editor.
- */
- public PrivateCommandPerformer getPrivateCommandPerformer() {
- return mConnection;
- }
-
- /**
- * Gets the expected index of the first char of the composing span within the editor's text.
- * Returns a negative value in case there appears to be no valid composing span.
- *
- * @see #getComposingLength()
- * @see RichInputConnection#hasSelection()
- * @see RichInputConnection#isCursorPositionKnown()
- * @see RichInputConnection#getExpectedSelectionStart()
- * @see RichInputConnection#getExpectedSelectionEnd()
- * @return The expected index in Java chars of the first char of the composing span.
- */
- // TODO: try and see if we can get rid of this method. Ideally the users of this class should
- // never need to know this.
- public int getComposingStart() {
- if (!mConnection.isCursorPositionKnown() || mConnection.hasSelection()) {
- return -1;
- }
- return mConnection.getExpectedSelectionStart() - mWordComposer.size();
- }
-
- /**
- * Gets the expected length in Java chars of the composing span.
- * May be 0 if there is no valid composing span.
- * @see #getComposingStart()
- * @return The expected length of the composing span.
- */
- // TODO: try and see if we can get rid of this method. Ideally the users of this class should
- // never need to know this.
- public int getComposingLength() {
- return mWordComposer.size();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
deleted file mode 100644
index ddc4ad99c..000000000
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.inputlogic;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-
-import com.android.inputmethod.compat.LooperCompatUtils;
-import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
-import com.android.inputmethod.latin.common.InputPointers;
-
-/**
- * A helper to manage deferred tasks for the input logic.
- */
-class InputLogicHandler implements Handler.Callback {
- final Handler mNonUIThreadHandler;
- // TODO: remove this reference.
- final LatinIME mLatinIME;
- final InputLogic mInputLogic;
- private final Object mLock = new Object();
- private boolean mInBatchInput; // synchronized using {@link #mLock}.
-
- private static final int MSG_GET_SUGGESTED_WORDS = 1;
-
- // A handler that never does anything. This is used for cases where events come before anything
- // is initialized, though probably only the monkey can actually do this.
- public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
- @Override
- public void reset() {}
- @Override
- public boolean handleMessage(final Message msg) { return true; }
- @Override
- public void onStartBatchInput() {}
- @Override
- public void onUpdateBatchInput(final InputPointers batchPointers,
- final int sequenceNumber) {}
- @Override
- public void onCancelBatchInput() {}
- @Override
- public void updateTailBatchInput(final InputPointers batchPointers,
- final int sequenceNumber) {}
- @Override
- public void getSuggestedWords(final int sessionId, final int sequenceNumber,
- final OnGetSuggestedWordsCallback callback) {}
- };
-
- InputLogicHandler() {
- mNonUIThreadHandler = null;
- mLatinIME = null;
- mInputLogic = null;
- }
-
- public InputLogicHandler(final LatinIME latinIME, final InputLogic inputLogic) {
- final HandlerThread handlerThread = new HandlerThread(
- InputLogicHandler.class.getSimpleName());
- handlerThread.start();
- mNonUIThreadHandler = new Handler(handlerThread.getLooper(), this);
- mLatinIME = latinIME;
- mInputLogic = inputLogic;
- }
-
- public void reset() {
- mNonUIThreadHandler.removeCallbacksAndMessages(null);
- }
-
- // In unit tests, we create several instances of LatinIME, which results in several instances
- // of InputLogicHandler. To avoid these handlers lingering, we call this.
- public void destroy() {
- LooperCompatUtils.quitSafely(mNonUIThreadHandler.getLooper());
- }
-
- /**
- * Handle a message.
- * @see android.os.Handler.Callback#handleMessage(android.os.Message)
- */
- // Called on the Non-UI handler thread by the Handler code.
- @Override
- public boolean handleMessage(final Message msg) {
- switch (msg.what) {
- case MSG_GET_SUGGESTED_WORDS:
- mLatinIME.getSuggestedWords(msg.arg1 /* inputStyle */,
- msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj);
- break;
- }
- return true;
- }
-
- // Called on the UI thread by InputLogic.
- public void onStartBatchInput() {
- synchronized (mLock) {
- mInBatchInput = true;
- }
- }
-
- public boolean isInBatchInput() {
- return mInBatchInput;
- }
-
- /**
- * Fetch suggestions corresponding to an update of a batch input.
- * @param batchPointers the updated pointers, including the part that was passed last time.
- * @param sequenceNumber the sequence number associated with this batch input.
- * @param isTailBatchInput true if this is the end of a batch input, false if it's an update.
- */
- // This method can be called from any thread and will see to it that the correct threads
- // are used for parts that require it. This method will send a message to the Non-UI handler
- // thread to pull suggestions, and get the inlined callback to get called on the Non-UI
- // handler thread. If this is the end of a batch input, the callback will then proceed to
- // send a message to the UI handler in LatinIME so that showing suggestions can be done on
- // the UI thread.
- private void updateBatchInput(final InputPointers batchPointers,
- final int sequenceNumber, final boolean isTailBatchInput) {
- synchronized (mLock) {
- if (!mInBatchInput) {
- // Batch input has ended or canceled while the message was being delivered.
- return;
- }
- mInputLogic.mWordComposer.setBatchInputPointers(batchPointers);
- final OnGetSuggestedWordsCallback callback = new OnGetSuggestedWordsCallback() {
- @Override
- public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
- showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput);
- }
- };
- getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH
- : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, callback);
- }
- }
-
- void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWordsForBatchInput,
- final boolean isTailBatchInput) {
- final SuggestedWords suggestedWordsToShowSuggestions;
- // We're now inside the callback. This always runs on the Non-UI thread,
- // no matter what thread updateBatchInput was originally called on.
- if (suggestedWordsForBatchInput.isEmpty()) {
- // Use old suggestions if we don't have any new ones.
- // Previous suggestions are found in InputLogic#mSuggestedWords.
- // Since these are the most recent ones and we just recomputed
- // new ones to update them, then the previous ones are there.
- suggestedWordsToShowSuggestions = mInputLogic.mSuggestedWords;
- } else {
- suggestedWordsToShowSuggestions = suggestedWordsForBatchInput;
- }
- mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWordsToShowSuggestions,
- isTailBatchInput /* dismissGestureFloatingPreviewText */);
- if (isTailBatchInput) {
- mInBatchInput = false;
- // The following call schedules onEndBatchInputInternal
- // to be called on the UI thread.
- mLatinIME.mHandler.showTailBatchInputResult(suggestedWordsToShowSuggestions);
- }
- }
-
- /**
- * Update a batch input.
- *
- * This fetches suggestions and updates the suggestion strip and the floating text preview.
- *
- * @param batchPointers the updated batch pointers.
- * @param sequenceNumber the sequence number associated with this batch input.
- */
- // Called on the UI thread by InputLogic.
- public void onUpdateBatchInput(final InputPointers batchPointers,
- final int sequenceNumber) {
- updateBatchInput(batchPointers, sequenceNumber, false /* isTailBatchInput */);
- }
-
- /**
- * Cancel a batch input.
- *
- * Note that as opposed to updateTailBatchInput, we do the UI side of this immediately on the
- * same thread, rather than get this to call a method in LatinIME. This is because
- * canceling a batch input does not necessitate the long operation of pulling suggestions.
- */
- // Called on the UI thread by InputLogic.
- public void onCancelBatchInput() {
- synchronized (mLock) {
- mInBatchInput = false;
- }
- }
-
- /**
- * Trigger an update for a tail batch input.
- *
- * A tail batch input is the last update for a gesture, the one that is triggered after the
- * user lifts their finger. This method schedules fetching suggestions on the non-UI thread,
- * then when the suggestions are computed it comes back on the UI thread to update the
- * suggestion strip, commit the first suggestion, and dismiss the floating text preview.
- *
- * @param batchPointers the updated batch pointers.
- * @param sequenceNumber the sequence number associated with this batch input.
- */
- // Called on the UI thread by InputLogic.
- public void updateTailBatchInput(final InputPointers batchPointers,
- final int sequenceNumber) {
- updateBatchInput(batchPointers, sequenceNumber, true /* isTailBatchInput */);
- }
-
- public void getSuggestedWords(final int inputStyle, final int sequenceNumber,
- final OnGetSuggestedWordsCallback callback) {
- mNonUIThreadHandler.obtainMessage(
- MSG_GET_SUGGESTED_WORDS, inputStyle, sequenceNumber, callback).sendToTarget();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
deleted file mode 100644
index 42eaa9c82..000000000
--- a/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.inputlogic;
-
-import android.os.Bundle;
-
-/**
- * Provides an interface matching
- * {@link android.view.inputmethod.InputConnection#performPrivateCommand(String,Bundle)}.
- */
-public interface PrivateCommandPerformer {
- /**
- * API to send private commands from an input method to its connected
- * editor. This can be used to provide domain-specific features that are
- * only known between certain input methods and their clients.
- *
- * @param action Name of the command to be performed. This must be a scoped
- * name, i.e. prefixed with a package name you own, so that
- * different developers will not create conflicting commands.
- * @param data Any data to include with the command.
- * @return true if the command was sent (regardless of whether the
- * associated editor understood it), false if the input connection is no
- * longer valid.
- */
- boolean performPrivateCommand(String action, Bundle data);
-}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/SpaceState.java b/java/src/com/android/inputmethod/latin/inputlogic/SpaceState.java
deleted file mode 100644
index ce80c0016..000000000
--- a/java/src/com/android/inputmethod/latin/inputlogic/SpaceState.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.inputlogic;
-
-/**
- * Class for managing space states.
- *
- * At any given time, the input logic is in one of five possible space states. Depending on the
- * current space state, some behavior will change; the prime example of this is the PHANTOM state,
- * in which any subsequent letter input will input a space before the letter. Read on the
- * description inside this class for each of the space states.
- */
-public class SpaceState {
- // None: the state where all the keyboard behavior is the most "standard" and no automatic
- // input is added or removed. In this state, all self-inserting keys only insert themselves,
- // and backspace removes one character.
- public static final int NONE = 0;
- // Double space: the state where the user pressed space twice quickly, which LatinIME
- // resolved as period-space. In this state, pressing backspace will undo the
- // double-space-to-period insertion: it will replace ". " with " ".
- public static final int DOUBLE = 1;
- // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
- // have just been swapped. In this state, pressing backspace will undo the swap: the
- // characters will be swapped back back, and the space state will go to WEAK.
- public static final int SWAP_PUNCTUATION = 2;
- // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
- // spaces happen when the user presses space, accepting the current suggestion (whether
- // it's an auto-correction or not). In this state, pressing a punctuation from the suggestion
- // strip inserts it before the space (while it inserts it after the space in the NONE state).
- public static final int WEAK = 3;
- // Phantom space: a not-yet-inserted space that should get inserted on the next input,
- // character provided it's not a separator. If it's a separator, the phantom space is dropped.
- // Phantom spaces happen when a user chooses a word from the suggestion strip. In this state,
- // non-separators insert a space before they get inserted.
- public static final int PHANTOM = 4;
-
- private SpaceState() {
- // This class is not publicly instantiable.
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
deleted file mode 100644
index 4d253b0cb..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Class representing dictionary header.
- */
-public final class DictionaryHeader {
- public final int mBodyOffset;
- @Nonnull
- public final DictionaryOptions mDictionaryOptions;
- @Nonnull
- public final FormatOptions mFormatOptions;
- @Nonnull
- public final String mLocaleString;
- @Nonnull
- public final String mVersionString;
- @Nonnull
- public final String mIdString;
-
- // Note that these are corresponding definitions in native code in latinime::HeaderPolicy
- // and latinime::HeaderReadWriteUtils.
- // TODO: Standardize the key names and bump up the format version, taking care not to
- // break format version 2 dictionaries.
- public static final String DICTIONARY_VERSION_KEY = "version";
- public static final String DICTIONARY_LOCALE_KEY = "locale";
- public static final String DICTIONARY_ID_KEY = "dictionary";
- public static final String DICTIONARY_DESCRIPTION_KEY = "description";
- public static final String DICTIONARY_DATE_KEY = "date";
- public static final String HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
- public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
- public static final String FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY =
- "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID";
- public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_ENTRY_COUNT";
- public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_ENTRY_COUNT";
- public static final String MAX_TRIGRAM_COUNT_KEY = "MAX_TRIGRAM_ENTRY_COUNT";
- public static final String ATTRIBUTE_VALUE_TRUE = "1";
- public static final String CODE_POINT_TABLE_KEY = "codePointTable";
-
- public DictionaryHeader(final int headerSize,
- @Nonnull final DictionaryOptions dictionaryOptions,
- @Nonnull final FormatOptions formatOptions) throws UnsupportedFormatException {
- mDictionaryOptions = dictionaryOptions;
- mFormatOptions = formatOptions;
- mBodyOffset = formatOptions.mVersion < FormatSpec.VERSION4 ? headerSize : 0;
- final String localeString = dictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_KEY);
- if (null == localeString) {
- throw new UnsupportedFormatException("Cannot create a FileHeader without a locale");
- }
- final String versionString = dictionaryOptions.mAttributes.get(DICTIONARY_VERSION_KEY);
- if (null == versionString) {
- throw new UnsupportedFormatException(
- "Cannot create a FileHeader without a version");
- }
- final String idString = dictionaryOptions.mAttributes.get(DICTIONARY_ID_KEY);
- if (null == idString) {
- throw new UnsupportedFormatException("Cannot create a FileHeader without an ID");
- }
- mLocaleString = localeString;
- mVersionString = versionString;
- mIdString = idString;
- }
-
- // Helper method to get the description
- @Nullable
- public String getDescription() {
- // TODO: Right now each dictionary file comes with a description in its own language.
- // It will display as is no matter the device's locale. It should be internationalized.
- return mDictionaryOptions.mAttributes.get(DICTIONARY_DESCRIPTION_KEY);
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
deleted file mode 100644
index 288261bf0..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-
-import java.util.Date;
-import java.util.HashMap;
-
-/**
- * Dictionary File Format Specification.
- */
-public final class FormatSpec {
-
- /*
- * File header layout is as follows:
- *
- * v |
- * e | MAGIC_NUMBER + version of the file format, 2 bytes.
- * r |
- * sion
- *
- * o |
- * p | not used, 2 bytes.
- * o |
- * nflags
- *
- * h |
- * e | size of the file header, 4bytes
- * a | including the size of the magic number, the option flags and the header size
- * d |
- * ersize
- *
- * attributes list
- *
- * attributes list is:
- * <key> = | string of characters at the char format described below, with the terminator used
- * | to signal the end of the string.
- * <value> = | string of characters at the char format described below, with the terminator used
- * | to signal the end of the string.
- * if the size of already read < headersize, goto key.
- *
- */
-
- /*
- * Node array (FusionDictionary.PtNodeArray) layout is as follows:
- *
- * n |
- * o | the number of PtNodes, 1 or 2 bytes.
- * d | 1 byte = bbbbbbbb match
- * e | case 1xxxxxxx => xxxxxxx << 8 + next byte
- * c | otherwise => bbbbbbbb
- * o |
- * unt
- *
- * n |
- * o | sequence of PtNodes,
- * d | the layout of each PtNode is described below.
- * e |
- * s
- *
- * f |
- * o | forward link address, 3byte
- * r | 1 byte = bbbbbbbb match
- * w | case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte)
- * a | otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte
- * r |
- * dlinkaddress
- */
-
- /* Node (FusionDictionary.PtNode) layout is as follows:
- * | CHILDREN_ADDRESS_TYPE 2 bits, 11 : FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
- * | 10 : FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES
- * f | 01 : FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE
- * l | 00 : FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS
- * a | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
- * g | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
- * s | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
- * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS
- * | is not a word ? 1 bit, 1 = yes, 0 = no : FLAG_IS_NOT_A_WORD
- * | is possibly offensive ? 1 bit, 1 = yes, 0 = no : FLAG_IS_POSSIBLY_OFFENSIVE
- *
- * c | IF FLAG_HAS_MULTIPLE_CHARS
- * h | char, char, char, char n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers
- * a | end 1 byte, = 0
- * r | ELSE
- * s | char 1 or 3 bytes
- * | END
- *
- * f |
- * r | IF FLAG_IS_TERMINAL
- * e | frequency 1 byte
- * q |
- *
- * c |
- * h | children address, CHILDREN_ADDRESS_TYPE bytes
- * i | This address is relative to the position of this field.
- * l |
- * drenaddress
- *
- * | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
- * | shortcut string list
- * | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS
- * | bigrams address list
- *
- * Char format is:
- * 1 byte = bbbbbbbb match
- * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
- * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
- * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
- * 00011111 would be outside unicode.
- * else: iso-latin-1 code
- * This allows for the whole unicode range to be encoded, including chars outside of
- * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
- * characters which should never happen anyway (and still work, but take 3 bytes).
- *
- * bigram address list is:
- * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
- * | addressSign = 1 bit, : FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE
- * | 1 = must take -address, 0 = must take +address
- * | xx : mask with MASK_BIGRAM_ATTR_ADDRESS_TYPE
- * | addressFormat = 2 bits, 00 = unused : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
- * | 01 = 1 byte : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
- * | 10 = 2 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES
- * | 11 = 3 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES
- * | 4 bits : frequency : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
- * <address> | IF (01 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE == addressFormat)
- * | read 1 byte, add top 4 bits
- * | ELSIF (10 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES == addressFormat)
- * | read 2 bytes, add top 4 bits
- * | ELSE // 11 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES == addressFormat
- * | read 3 bytes, add top 4 bits
- * | END
- * | if (FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) then address = -address
- * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) goto bigram_and_shortcut_address_list_is
- *
- * shortcut string list is:
- * <byte size> = PTNODE_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
- * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
- * | reserved = 3 bits, must be 0
- * | 4 bits : frequency : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
- * <shortcut> = | string of characters at the char format described above, with the terminator
- * | used to signal the end of the string.
- * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT goto flags
- */
-
- public static final int MAGIC_NUMBER = 0x9BC13AFE;
- static final int NOT_A_VERSION_NUMBER = -1;
-
- // These MUST have the same values as the relevant constants in format_utils.h.
- // From version 2.01 on, we use version * 100 + revision as a version number. That allows
- // us to change the format during development while having testing devices remove
- // older files with each upgrade, while still having a readable versioning scheme.
- // When we bump up the dictionary format version, we should update
- // ExpandableDictionary.needsToMigrateDictionary() and
- // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType().
- public static final int VERSION2 = 2;
- public static final int VERSION201 = 201;
- public static final int VERSION202 = 202;
- // format version for Fava Dictionaries.
- public static final int VERSION_DELIGHT3 = 86736212;
- public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201;
- // Dictionary version used for testing.
- public static final int VERSION4_ONLY_FOR_TESTING = 399;
- public static final int VERSION402 = 402;
- public static final int VERSION403 = 403;
- public static final int VERSION4 = VERSION403;
- public static final int MINIMUM_SUPPORTED_STATIC_VERSION = VERSION202;
- public static final int MAXIMUM_SUPPORTED_STATIC_VERSION = VERSION_DELIGHT3;
- static final int MINIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4;
- static final int MAXIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION403;
-
- // TODO: Make this value adaptative to content data, store it in the header, and
- // use it in the reading code.
- static final int MAX_WORD_LENGTH = DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH;
-
- // These flags are used only in the static dictionary.
- static final int MASK_CHILDREN_ADDRESS_TYPE = 0xC0;
- static final int FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS = 0x00;
- static final int FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE = 0x40;
- static final int FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES = 0x80;
- static final int FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = 0xC0;
-
- static final int FLAG_HAS_MULTIPLE_CHARS = 0x20;
-
- static final int FLAG_IS_TERMINAL = 0x10;
- static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08;
- static final int FLAG_HAS_BIGRAMS = 0x04;
- static final int FLAG_IS_NOT_A_WORD = 0x02;
- static final int FLAG_IS_POSSIBLY_OFFENSIVE = 0x01;
-
- static final int FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT = 0x80;
- static final int FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE = 0x40;
- static final int MASK_BIGRAM_ATTR_ADDRESS_TYPE = 0x30;
- static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE = 0x10;
- static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES = 0x20;
- static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES = 0x30;
- static final int FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY = 0x0F;
-
- static final int PTNODE_CHARACTERS_TERMINATOR = 0x1F;
-
- static final int PTNODE_TERMINATOR_SIZE = 1;
- static final int PTNODE_FLAGS_SIZE = 1;
- static final int PTNODE_FREQUENCY_SIZE = 1;
- static final int PTNODE_MAX_ADDRESS_SIZE = 3;
- static final int PTNODE_ATTRIBUTE_FLAGS_SIZE = 1;
- static final int PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
- static final int PTNODE_SHORTCUT_LIST_SIZE_SIZE = 2;
-
- static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
- static final int INVALID_CHARACTER = -1;
-
- static final int MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT = 0x7F; // 127
- // Large PtNode array size field size is 2 bytes.
- static final int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000;
- static final int MAX_PTNODES_IN_A_PT_NODE_ARRAY = 0x7FFF; // 32767
- static final int MAX_BIGRAMS_IN_A_PTNODE = 10000;
- static final int MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE = 0xFFFF;
-
- static final int MAX_TERMINAL_FREQUENCY = 255;
- static final int MAX_BIGRAM_FREQUENCY = 15;
-
- public static final int SHORTCUT_WHITELIST_FREQUENCY = 15;
-
- // This option needs to be the same numeric value as the one in binary_format.h.
- static final int NOT_VALID_WORD = -99;
-
- static final int UINT8_MAX = 0xFF;
- static final int UINT16_MAX = 0xFFFF;
- static final int UINT24_MAX = 0xFFFFFF;
- static final int MSB8 = 0x80;
- static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
- static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
-
- /**
- * Options about file format.
- */
- public static final class FormatOptions {
- public final int mVersion;
- public final boolean mHasTimestamp;
-
- @UsedForTesting
- public FormatOptions(final int version) {
- this(version, false /* hasTimestamp */);
- }
-
- public FormatOptions(final int version, final boolean hasTimestamp) {
- mVersion = version;
- mHasTimestamp = hasTimestamp;
- }
- }
-
- /**
- * Options global to the dictionary.
- */
- public static final class DictionaryOptions {
- public final HashMap<String, String> mAttributes;
- public DictionaryOptions(final HashMap<String, String> attributes) {
- mAttributes = attributes;
- }
- @Override
- public String toString() { // Convenience method
- return toString(0, false);
- }
- public String toString(final int indentCount, final boolean plumbing) {
- final StringBuilder indent = new StringBuilder();
- if (plumbing) {
- indent.append("H:");
- } else {
- for (int i = 0; i < indentCount; ++i) {
- indent.append(" ");
- }
- }
- final StringBuilder s = new StringBuilder();
- for (final String optionKey : mAttributes.keySet()) {
- s.append(indent);
- s.append(optionKey);
- s.append(" = ");
- if ("date".equals(optionKey) && !plumbing) {
- // Date needs a number of milliseconds, but the dictionary contains seconds
- s.append(new Date(
- 1000 * Long.parseLong(mAttributes.get(optionKey))).toString());
- } else {
- s.append(mAttributes.get(optionKey));
- }
- s.append("\n");
- }
- return s.toString();
- }
- }
-
- private FormatSpec() {
- // This utility class is not publicly instantiable.
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java
deleted file mode 100644
index b1d19dc3c..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.latin.NgramContext;
-
-public class NgramProperty {
- public final WeightedString mTargetWord;
- public final NgramContext mNgramContext;
-
- public NgramProperty(final WeightedString targetWord, final NgramContext ngramContext) {
- mTargetWord = targetWord;
- mNgramContext = ngramContext;
- }
-
- @Override
- public int hashCode() {
- return mTargetWord.hashCode() ^ mNgramContext.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) return true;
- if (!(o instanceof NgramProperty)) return false;
- final NgramProperty n = (NgramProperty)o;
- return mTargetWord.equals(n.mTargetWord) && mNgramContext.equals(n.mNgramContext);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
deleted file mode 100644
index 03c2ece1d..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.utils.CombinedFormatUtils;
-
-import java.util.Arrays;
-
-public final class ProbabilityInfo {
- public final int mProbability;
- // mTimestamp, mLevel and mCount are historical info. These values are depend on the
- // implementation in native code; thus, we must not use them and have any assumptions about
- // them except for tests.
- public final int mTimestamp;
- public final int mLevel;
- public final int mCount;
-
- @UsedForTesting
- public static ProbabilityInfo max(final ProbabilityInfo probabilityInfo1,
- final ProbabilityInfo probabilityInfo2) {
- if (probabilityInfo1 == null) {
- return probabilityInfo2;
- }
- if (probabilityInfo2 == null) {
- return probabilityInfo1;
- }
- return (probabilityInfo1.mProbability > probabilityInfo2.mProbability) ? probabilityInfo1
- : probabilityInfo2;
- }
-
- public ProbabilityInfo(final int probability) {
- this(probability, BinaryDictionary.NOT_A_VALID_TIMESTAMP, 0, 0);
- }
-
- public ProbabilityInfo(final int probability, final int timestamp, final int level,
- final int count) {
- mProbability = probability;
- mTimestamp = timestamp;
- mLevel = level;
- mCount = count;
- }
-
- public boolean hasHistoricalInfo() {
- return mTimestamp != BinaryDictionary.NOT_A_VALID_TIMESTAMP;
- }
-
- @Override
- public int hashCode() {
- if (hasHistoricalInfo()) {
- return Arrays.hashCode(new Object[] { mProbability, mTimestamp, mLevel, mCount });
- }
- return Arrays.hashCode(new Object[] { mProbability });
- }
-
- @Override
- public String toString() {
- return CombinedFormatUtils.formatProbabilityInfo(this);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) return true;
- if (!(o instanceof ProbabilityInfo)) return false;
- final ProbabilityInfo p = (ProbabilityInfo)o;
- if (!hasHistoricalInfo() && !p.hasHistoricalInfo()) {
- return mProbability == p.mProbability;
- }
- return mProbability == p.mProbability && mTimestamp == p.mTimestamp && mLevel == p.mLevel
- && mCount == p.mCount;
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java b/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
deleted file mode 100644
index 4f3861526..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-/**
- * Simple exception thrown when a file format is not recognized.
- */
-public final class UnsupportedFormatException extends Exception {
- public UnsupportedFormatException(String description) {
- super(description);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/WeightedString.java b/java/src/com/android/inputmethod/latin/makedict/WeightedString.java
deleted file mode 100644
index f6782df9e..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/WeightedString.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.util.Arrays;
-
-/**
- * A string with a probability.
- *
- * This represents an "attribute", that is either a bigram or a shortcut.
- */
-public final class WeightedString {
- public final String mWord;
- public ProbabilityInfo mProbabilityInfo;
-
- public WeightedString(final String word, final int probability) {
- this(word, new ProbabilityInfo(probability));
- }
-
- public WeightedString(final String word, final ProbabilityInfo probabilityInfo) {
- mWord = word;
- mProbabilityInfo = probabilityInfo;
- }
-
- @UsedForTesting
- public int getProbability() {
- return mProbabilityInfo.mProbability;
- }
-
- public void setProbability(final int probability) {
- mProbabilityInfo = new ProbabilityInfo(probability);
- }
-
- @Override
- public int hashCode() {
- return Arrays.hashCode(new Object[] { mWord, mProbabilityInfo});
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) return true;
- if (!(o instanceof WeightedString)) return false;
- final WeightedString w = (WeightedString)o;
- return mWord.equals(w.mWord) && mProbabilityInfo.equals(w.mProbabilityInfo);
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
deleted file mode 100644
index 264e75710..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.NgramContext.WordInfo;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.CombinedFormatUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import javax.annotation.Nullable;
-
-/**
- * Utility class for a word with a probability.
- *
- * This is chiefly used to iterate a dictionary.
- */
-public final class WordProperty implements Comparable<WordProperty> {
- public final String mWord;
- public final ProbabilityInfo mProbabilityInfo;
- public final ArrayList<NgramProperty> mNgrams;
- // TODO: Support mIsBeginningOfSentence.
- public final boolean mIsBeginningOfSentence;
- public final boolean mIsNotAWord;
- public final boolean mIsPossiblyOffensive;
- public final boolean mHasNgrams;
-
- private int mHashCode = 0;
-
- // TODO: Support n-gram.
- @UsedForTesting
- public WordProperty(final String word, final ProbabilityInfo probabilityInfo,
- @Nullable final ArrayList<WeightedString> bigrams,
- final boolean isNotAWord, final boolean isPossiblyOffensive) {
- mWord = word;
- mProbabilityInfo = probabilityInfo;
- if (null == bigrams) {
- mNgrams = null;
- } else {
- mNgrams = new ArrayList<>();
- final NgramContext ngramContext = new NgramContext(new WordInfo(mWord));
- for (final WeightedString bigramTarget : bigrams) {
- mNgrams.add(new NgramProperty(bigramTarget, ngramContext));
- }
- }
- mIsBeginningOfSentence = false;
- mIsNotAWord = isNotAWord;
- mIsPossiblyOffensive = isPossiblyOffensive;
- mHasNgrams = bigrams != null && !bigrams.isEmpty();
- }
-
- private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) {
- return new ProbabilityInfo(
- probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX],
- probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX],
- probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX],
- probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]);
- }
-
- // Construct word property using information from native code.
- // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY.
- public WordProperty(final int[] codePoints, final boolean isNotAWord,
- final boolean isPossiblyOffensive, final boolean hasBigram,
- final boolean isBeginningOfSentence, final int[] probabilityInfo,
- final ArrayList<int[][]> ngramPrevWordsArray,
- final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray,
- final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo) {
- mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
- mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
- final ArrayList<NgramProperty> ngrams = new ArrayList<>();
- mIsBeginningOfSentence = isBeginningOfSentence;
- mIsNotAWord = isNotAWord;
- mIsPossiblyOffensive = isPossiblyOffensive;
- mHasNgrams = hasBigram;
-
- final int relatedNgramCount = ngramTargets.size();
- for (int i = 0; i < relatedNgramCount; i++) {
- final String ngramTargetString =
- StringUtils.getStringFromNullTerminatedCodePointArray(ngramTargets.get(i));
- final WeightedString ngramTarget = new WeightedString(ngramTargetString,
- createProbabilityInfoFromArray(ngramProbabilityInfo.get(i)));
- final int[][] prevWords = ngramPrevWordsArray.get(i);
- final boolean[] isBeginningOfSentenceArray =
- ngramPrevWordIsBeginningOfSentenceArray.get(i);
- final WordInfo[] wordInfoArray = new WordInfo[prevWords.length];
- for (int j = 0; j < prevWords.length; j++) {
- wordInfoArray[j] = isBeginningOfSentenceArray[j]
- ? WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO
- : new WordInfo(StringUtils.getStringFromNullTerminatedCodePointArray(
- prevWords[j]));
- }
- final NgramContext ngramContext = new NgramContext(wordInfoArray);
- ngrams.add(new NgramProperty(ngramTarget, ngramContext));
- }
- mNgrams = ngrams.isEmpty() ? null : ngrams;
- }
-
- // TODO: Remove
- @UsedForTesting
- public ArrayList<WeightedString> getBigrams() {
- if (null == mNgrams) {
- return null;
- }
- final ArrayList<WeightedString> bigrams = new ArrayList<>();
- for (final NgramProperty ngram : mNgrams) {
- if (ngram.mNgramContext.getPrevWordCount() == 1) {
- bigrams.add(ngram.mTargetWord);
- }
- }
- return bigrams;
- }
-
- public int getProbability() {
- return mProbabilityInfo.mProbability;
- }
-
- private static int computeHashCode(WordProperty word) {
- return Arrays.hashCode(new Object[] {
- word.mWord,
- word.mProbabilityInfo,
- word.mNgrams,
- word.mIsNotAWord,
- word.mIsPossiblyOffensive
- });
- }
-
- /**
- * Three-way comparison.
- *
- * A Word x is greater than a word y if x has a higher frequency. If they have the same
- * frequency, they are sorted in lexicographic order.
- */
- @Override
- public int compareTo(final WordProperty w) {
- if (getProbability() < w.getProbability()) return 1;
- if (getProbability() > w.getProbability()) return -1;
- return mWord.compareTo(w.mWord);
- }
-
- /**
- * Equality test.
- *
- * Words are equal if they have the same frequency, the same spellings, and the same
- * attributes.
- */
- @Override
- public boolean equals(Object o) {
- if (o == this) return true;
- if (!(o instanceof WordProperty)) return false;
- WordProperty w = (WordProperty)o;
- return mProbabilityInfo.equals(w.mProbabilityInfo)
- && mWord.equals(w.mWord) && equals(mNgrams, w.mNgrams)
- && mIsNotAWord == w.mIsNotAWord && mIsPossiblyOffensive == w.mIsPossiblyOffensive
- && mHasNgrams == w.mHasNgrams;
- }
-
- // TDOO: Have a utility method like java.util.Objects.equals.
- private static <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) {
- if (null == a) {
- return null == b;
- }
- return a.equals(b);
- }
-
- @Override
- public int hashCode() {
- if (mHashCode == 0) {
- mHashCode = computeHashCode(this);
- }
- return mHashCode;
- }
-
- @UsedForTesting
- public boolean isValid() {
- return getProbability() != Dictionary.NOT_A_PROBABILITY;
- }
-
- @Override
- public String toString() {
- return CombinedFormatUtils.formatWordProperty(this);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/network/AuthException.java b/java/src/com/android/inputmethod/latin/network/AuthException.java
deleted file mode 100644
index 1bce4c156..000000000
--- a/java/src/com/android/inputmethod/latin/network/AuthException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.network;
-
-/**
- * Authentication exception. When this exception is thrown, the client may
- * try to refresh the authentication token and try again.
- */
-public class AuthException extends Exception {
- public AuthException() {
- super();
- }
-
- public AuthException(Throwable throwable) {
- super(throwable);
- }
-
- public AuthException(String detailMessage) {
- super(detailMessage);
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java
deleted file mode 100644
index 079d07eac..000000000
--- a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.network;
-
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * A client for executing HTTP requests synchronously.
- * This must never be called from the main thread.
- */
-public class BlockingHttpClient {
- private static final boolean DEBUG = false;
- private static final String TAG = BlockingHttpClient.class.getSimpleName();
-
- private final HttpURLConnection mConnection;
-
- /**
- * Interface that handles processing the response for a request.
- */
- public interface ResponseProcessor<T> {
- /**
- * Called when the HTTP request finishes successfully.
- * The {@link InputStream} is closed by the client after the method finishes,
- * so any processing must be done in this method itself.
- *
- * @param response An input stream that can be used to read the HTTP response.
- */
- T onSuccess(InputStream response) throws IOException;
- }
-
- public BlockingHttpClient(HttpURLConnection connection) {
- mConnection = connection;
- }
-
- /**
- * Executes the request on the underlying {@link HttpURLConnection}.
- *
- * @param request The request payload, if any, or null.
- * @param responseProcessor A processor for the HTTP response.
- */
- public <T> T execute(@Nullable byte[] request, @Nonnull ResponseProcessor<T> responseProcessor)
- throws IOException, AuthException, HttpException {
- if (DEBUG) {
- Log.d(TAG, "execute: " + mConnection.getURL());
- }
- try {
- if (request != null) {
- if (DEBUG) {
- Log.d(TAG, "request size: " + request.length);
- }
- OutputStream out = new BufferedOutputStream(mConnection.getOutputStream());
- out.write(request);
- out.flush();
- out.close();
- }
-
- final int responseCode = mConnection.getResponseCode();
- if (responseCode != HttpURLConnection.HTTP_OK) {
- Log.w(TAG, "Response error: " + responseCode + ", Message: "
- + mConnection.getResponseMessage());
- if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
- throw new AuthException(mConnection.getResponseMessage());
- }
- throw new HttpException(responseCode);
- }
- if (DEBUG) {
- Log.d(TAG, "request executed successfully");
- }
- return responseProcessor.onSuccess(mConnection.getInputStream());
- } finally {
- mConnection.disconnect();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/network/HttpException.java b/java/src/com/android/inputmethod/latin/network/HttpException.java
deleted file mode 100644
index b9d8b6372..000000000
--- a/java/src/com/android/inputmethod/latin/network/HttpException.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.network;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-/**
- * The HttpException exception represents a XML/HTTP fault with a HTTP status code.
- */
-public class HttpException extends Exception {
-
- /**
- * The HTTP status code.
- */
- private final int mStatusCode;
-
- /**
- * @param statusCode int HTTP status code.
- */
- public HttpException(int statusCode) {
- super("Response Code: " + statusCode);
- mStatusCode = statusCode;
- }
-
- /**
- * @return the HTTP status code related to this exception.
- */
- @UsedForTesting
- public int getHttpStatusCode() {
- return mStatusCode;
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java
deleted file mode 100644
index df54bf464..000000000
--- a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.network;
-
-import android.text.TextUtils;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map.Entry;
-
-/**
- * Builder for {@link HttpURLConnection}s.
- *
- * TODO: Remove @UsedForTesting after this is actually used.
- */
-@UsedForTesting
-public class HttpUrlConnectionBuilder {
- private static final int DEFAULT_TIMEOUT_MILLIS = 5 * 1000;
-
- /**
- * Request header key for authentication.
- */
- public static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
-
- /**
- * Request header key for cache control.
- */
- public static final String KEY_CACHE_CONTROL = "Cache-Control";
- /**
- * Request header value for cache control indicating no caching.
- * @see #KEY_CACHE_CONTROL
- */
- public static final String VALUE_NO_CACHE = "no-cache";
-
- /**
- * Indicates that the request is unidirectional - upload-only.
- * TODO: Remove @UsedForTesting after this is actually used.
- */
- @UsedForTesting
- public static final int MODE_UPLOAD_ONLY = 1;
- /**
- * Indicates that the request is unidirectional - download only.
- * TODO: Remove @UsedForTesting after this is actually used.
- */
- @UsedForTesting
- public static final int MODE_DOWNLOAD_ONLY = 2;
- /**
- * Indicates that the request is bi-directional.
- * TODO: Remove @UsedForTesting after this is actually used.
- */
- @UsedForTesting
- public static final int MODE_BI_DIRECTIONAL = 3;
-
- private final HashMap<String, String> mHeaderMap = new HashMap<>();
-
- private URL mUrl;
- private int mConnectTimeoutMillis = DEFAULT_TIMEOUT_MILLIS;
- private int mReadTimeoutMillis = DEFAULT_TIMEOUT_MILLIS;
- private int mContentLength = -1;
- private boolean mUseCache;
- private int mMode;
-
- /**
- * Sets the URL that'll be used for the request.
- * This *must* be set before calling {@link #build()}
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setUrl(String url) throws MalformedURLException {
- if (TextUtils.isEmpty(url)) {
- throw new IllegalArgumentException("URL must not be empty");
- }
- mUrl = new URL(url);
- return this;
- }
-
- /**
- * Sets the connect timeout. Defaults to {@value #DEFAULT_TIMEOUT_MILLIS} milliseconds.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setConnectTimeout(int timeoutMillis) {
- if (timeoutMillis < 0) {
- throw new IllegalArgumentException("connect-timeout must be >= 0, but was "
- + timeoutMillis);
- }
- mConnectTimeoutMillis = timeoutMillis;
- return this;
- }
-
- /**
- * Sets the read timeout. Defaults to {@value #DEFAULT_TIMEOUT_MILLIS} milliseconds.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setReadTimeout(int timeoutMillis) {
- if (timeoutMillis < 0) {
- throw new IllegalArgumentException("read-timeout must be >= 0, but was "
- + timeoutMillis);
- }
- mReadTimeoutMillis = timeoutMillis;
- return this;
- }
-
- /**
- * Adds an entry to the request header.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder addHeader(String key, String value) {
- mHeaderMap.put(key, value);
- return this;
- }
-
- /**
- * Sets an authentication token.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setAuthToken(String value) {
- mHeaderMap.put(HTTP_HEADER_AUTHORIZATION, value);
- return this;
- }
-
- /**
- * Sets the request to be executed such that the input is not buffered.
- * This may be set when the request size is known beforehand.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setFixedLengthForStreaming(int length) {
- mContentLength = length;
- return this;
- }
-
- /**
- * Indicates if the request can use cached responses or not.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setUseCache(boolean useCache) {
- mUseCache = useCache;
- return this;
- }
-
- /**
- * The request mode.
- * Sets the request mode to be one of: upload-only, download-only or bidirectional.
- *
- * @see #MODE_UPLOAD_ONLY
- * @see #MODE_DOWNLOAD_ONLY
- * @see #MODE_BI_DIRECTIONAL
- *
- * TODO: Remove @UsedForTesting after this method is actually used
- */
- @UsedForTesting
- public HttpUrlConnectionBuilder setMode(int mode) {
- if (mode != MODE_UPLOAD_ONLY
- && mode != MODE_DOWNLOAD_ONLY
- && mode != MODE_BI_DIRECTIONAL) {
- throw new IllegalArgumentException("Invalid mode specified:" + mode);
- }
- mMode = mode;
- return this;
- }
-
- /**
- * Builds the {@link HttpURLConnection} instance that can be used to execute the request.
- *
- * TODO: Remove @UsedForTesting after this method is actually used.
- */
- @UsedForTesting
- public HttpURLConnection build() throws IOException {
- if (mUrl == null) {
- throw new IllegalArgumentException("A URL must be specified!");
- }
- final HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
- connection.setConnectTimeout(mConnectTimeoutMillis);
- connection.setReadTimeout(mReadTimeoutMillis);
- connection.setUseCaches(mUseCache);
- switch (mMode) {
- case MODE_UPLOAD_ONLY:
- connection.setDoInput(true);
- connection.setDoOutput(false);
- break;
- case MODE_DOWNLOAD_ONLY:
- connection.setDoInput(false);
- connection.setDoOutput(true);
- break;
- case MODE_BI_DIRECTIONAL:
- connection.setDoInput(true);
- connection.setDoOutput(true);
- break;
- }
- for (final Entry<String, String> entry : mHeaderMap.entrySet()) {
- connection.addRequestProperty(entry.getKey(), entry.getValue());
- }
- if (mContentLength >= 0) {
- connection.setFixedLengthStreamingMode(mContentLength);
- }
- return connection;
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/permissions/PermissionsActivity.java b/java/src/com/android/inputmethod/latin/permissions/PermissionsActivity.java
deleted file mode 100644
index 36d8ed943..000000000
--- a/java/src/com/android/inputmethod/latin/permissions/PermissionsActivity.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.inputmethod.latin.permissions;
-
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.core.app.ActivityCompat;
-
-/**
- * An activity to help request permissions. It's used when no other activity is available, e.g. in
- * InputMethodService. This activity assumes that all permissions are not granted yet.
- */
-public final class PermissionsActivity
- extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback {
-
- /**
- * Key to retrieve requested permissions from the intent.
- */
- public static final String EXTRA_PERMISSION_REQUESTED_PERMISSIONS = "requested_permissions";
-
- /**
- * Key to retrieve request code from the intent.
- */
- public static final String EXTRA_PERMISSION_REQUEST_CODE = "request_code";
-
- private static final int INVALID_REQUEST_CODE = -1;
-
- private int mPendingRequestCode = INVALID_REQUEST_CODE;
-
- /**
- * Starts a PermissionsActivity and checks/requests supplied permissions.
- */
- public static void run(
- @NonNull Context context, int requestCode, @NonNull String... permissionStrings) {
- Intent intent = new Intent(context.getApplicationContext(), PermissionsActivity.class);
- intent.putExtra(EXTRA_PERMISSION_REQUESTED_PERMISSIONS, permissionStrings);
- intent.putExtra(EXTRA_PERMISSION_REQUEST_CODE, requestCode);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- context.startActivity(intent);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mPendingRequestCode = (savedInstanceState != null)
- ? savedInstanceState.getInt(EXTRA_PERMISSION_REQUEST_CODE, INVALID_REQUEST_CODE)
- : INVALID_REQUEST_CODE;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(EXTRA_PERMISSION_REQUEST_CODE, mPendingRequestCode);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- // Only do request when there is no pending request to avoid duplicated requests.
- if (mPendingRequestCode == INVALID_REQUEST_CODE) {
- final Bundle extras = getIntent().getExtras();
- final String[] permissionsToRequest =
- extras.getStringArray(EXTRA_PERMISSION_REQUESTED_PERMISSIONS);
- mPendingRequestCode = extras.getInt(EXTRA_PERMISSION_REQUEST_CODE);
- // Assuming that all supplied permissions are not granted yet, so that we don't need to
- // check them again.
- PermissionsUtil.requestPermissions(this, mPendingRequestCode, permissionsToRequest);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(
- int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- mPendingRequestCode = INVALID_REQUEST_CODE;
- PermissionsManager.get(this).onRequestPermissionsResult(
- requestCode, permissions, grantResults);
- finish();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/permissions/PermissionsManager.java b/java/src/com/android/inputmethod/latin/permissions/PermissionsManager.java
deleted file mode 100644
index 08c623ab5..000000000
--- a/java/src/com/android/inputmethod/latin/permissions/PermissionsManager.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.inputmethod.latin.permissions;
-
-import android.app.Activity;
-import android.content.Context;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Manager to perform permission related tasks. Always call on the UI thread.
- */
-public class PermissionsManager {
-
- public interface PermissionsResultCallback {
- void onRequestPermissionsResult(boolean allGranted);
- }
-
- private int mRequestCodeId;
-
- private final Context mContext;
- private final Map<Integer, PermissionsResultCallback> mRequestIdToCallback = new HashMap<>();
-
- private static PermissionsManager sInstance;
-
- public PermissionsManager(Context context) {
- mContext = context;
- }
-
- @Nonnull
- public static synchronized PermissionsManager get(@Nonnull Context context) {
- if (sInstance == null) {
- sInstance = new PermissionsManager(context);
- }
- return sInstance;
- }
-
- private synchronized int getNextRequestId() {
- return ++mRequestCodeId;
- }
-
-
- public synchronized void requestPermissions(@Nonnull PermissionsResultCallback callback,
- @Nullable Activity activity,
- String... permissionsToRequest) {
- List<String> deniedPermissions = PermissionsUtil.getDeniedPermissions(
- mContext, permissionsToRequest);
- if (deniedPermissions.isEmpty()) {
- return;
- }
- // otherwise request the permissions.
- int requestId = getNextRequestId();
- String[] permissionsArray = deniedPermissions.toArray(
- new String[deniedPermissions.size()]);
-
- mRequestIdToCallback.put(requestId, callback);
- if (activity != null) {
- PermissionsUtil.requestPermissions(activity, requestId, permissionsArray);
- } else {
- PermissionsActivity.run(mContext, requestId, permissionsArray);
- }
- }
-
- public synchronized void onRequestPermissionsResult(
- int requestCode, String[] permissions, int[] grantResults) {
- PermissionsResultCallback permissionsResultCallback = mRequestIdToCallback.get(requestCode);
- mRequestIdToCallback.remove(requestCode);
-
- boolean allGranted = PermissionsUtil.allGranted(grantResults);
- permissionsResultCallback.onRequestPermissionsResult(allGranted);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/permissions/PermissionsUtil.java b/java/src/com/android/inputmethod/latin/permissions/PermissionsUtil.java
deleted file mode 100644
index 9a618a755..000000000
--- a/java/src/com/android/inputmethod/latin/permissions/PermissionsUtil.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.inputmethod.latin.permissions;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import androidx.annotation.NonNull;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Utility class for permissions.
- */
-public class PermissionsUtil {
-
- /**
- * Returns the list of permissions not granted from the given list of permissions.
- * @param context Context
- * @param permissions list of permissions to check.
- * @return the list of permissions that do not have permission to use.
- */
- public static List<String> getDeniedPermissions(Context context,
- String... permissions) {
- final List<String> deniedPermissions = new ArrayList<>();
- for (String permission : permissions) {
- if (ContextCompat.checkSelfPermission(context, permission)
- != PackageManager.PERMISSION_GRANTED) {
- deniedPermissions.add(permission);
- }
- }
- return deniedPermissions;
- }
-
- /**
- * Uses the given activity and requests the user for permissions.
- * @param activity activity to use.
- * @param requestCode request code/id to use.
- * @param permissions String array of permissions that needs to be requested.
- */
- public static void requestPermissions(Activity activity, int requestCode,
- String[] permissions) {
- ActivityCompat.requestPermissions(activity, permissions, requestCode);
- }
-
- /**
- * Checks if all the permissions are granted.
- */
- public static boolean allGranted(@NonNull int[] grantResults) {
- for (int result : grantResults) {
- if (result != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Queries if al the permissions are granted for the given permission strings.
- */
- public static boolean checkAllPermissionsGranted(Context context, String... permissions) {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- // For all pre-M devices, we should have all the premissions granted on install.
- return true;
- }
-
- for (String permission : permissions) {
- if (ContextCompat.checkSelfPermission(context, permission)
- != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
deleted file mode 100644
index ab3ef964e..000000000
--- a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.personalization;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.Context;
-import android.util.Patterns;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-public class AccountUtils {
- private AccountUtils() {
- // This utility class is not publicly instantiable.
- }
-
- private static Account[] getAccounts(final Context context) {
- return AccountManager.get(context).getAccounts();
- }
-
- public static List<String> getDeviceAccountsEmailAddresses(final Context context) {
- final ArrayList<String> retval = new ArrayList<>();
- for (final Account account : getAccounts(context)) {
- final String name = account.name;
- if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) {
- retval.add(name);
- retval.add(name.split("@")[0]);
- }
- }
- return retval;
- }
-
- /**
- * Get all device accounts having specified domain name.
- * @param context application context
- * @param domain domain name used for filtering
- * @return List of account names that contain the specified domain name
- */
- public static List<String> getDeviceAccountsWithDomain(
- final Context context, final String domain) {
- final ArrayList<String> retval = new ArrayList<>();
- final String atDomain = "@" + domain.toLowerCase(Locale.ROOT);
- for (final Account account : getAccounts(context)) {
- if (account.name.toLowerCase(Locale.ROOT).endsWith(atDomain)) {
- retval.add(account.name);
- }
- }
- return retval;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
deleted file mode 100644
index 298e46c0a..000000000
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.personalization;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.android.inputmethod.latin.common.FileUtils;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.lang.ref.SoftReference;
-import java.util.Locale;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Helps handle and manage personalized dictionaries such as {@link UserHistoryDictionary}.
- */
-public class PersonalizationHelper {
- private static final String TAG = PersonalizationHelper.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
- sLangUserHistoryDictCache = new ConcurrentHashMap<>();
-
- @Nonnull
- public static UserHistoryDictionary getUserHistoryDictionary(
- final Context context, final Locale locale, @Nullable final String accountName) {
- String lookupStr = locale.toString();
- if (accountName != null) {
- lookupStr += "." + accountName;
- }
- synchronized (sLangUserHistoryDictCache) {
- if (sLangUserHistoryDictCache.containsKey(lookupStr)) {
- final SoftReference<UserHistoryDictionary> ref =
- sLangUserHistoryDictCache.get(lookupStr);
- final UserHistoryDictionary dict = ref == null ? null : ref.get();
- if (dict != null) {
- if (DEBUG) {
- Log.d(TAG, "Use cached UserHistoryDictionary with lookup: " + lookupStr);
- }
- dict.reloadDictionaryIfRequired();
- return dict;
- }
- }
- final UserHistoryDictionary dict = new UserHistoryDictionary(
- context, locale, accountName);
- sLangUserHistoryDictCache.put(lookupStr, new SoftReference<>(dict));
- return dict;
- }
- }
-
- public static void removeAllUserHistoryDictionaries(final Context context) {
- synchronized (sLangUserHistoryDictCache) {
- for (final ConcurrentHashMap.Entry<String, SoftReference<UserHistoryDictionary>> entry
- : sLangUserHistoryDictCache.entrySet()) {
- if (entry.getValue() != null) {
- final UserHistoryDictionary dict = entry.getValue().get();
- if (dict != null) {
- dict.clear();
- }
- }
- }
- sLangUserHistoryDictCache.clear();
- final File filesDir = context.getFilesDir();
- if (filesDir == null) {
- Log.e(TAG, "context.getFilesDir() returned null.");
- return;
- }
- final boolean filesDeleted = FileUtils.deleteFilteredFiles(
- filesDir, new DictFilter(UserHistoryDictionary.NAME));
- if (!filesDeleted) {
- Log.e(TAG, "Cannot remove dictionary files. filesDir: " + filesDir.getAbsolutePath()
- + ", dictNamePrefix: " + UserHistoryDictionary.NAME);
- }
- }
- }
-
- private static class DictFilter implements FilenameFilter {
- private final String mName;
-
- DictFilter(final String name) {
- mName = name;
- }
-
- @Override
- public boolean accept(final File dir, final String name) {
- return name.startsWith(mName);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
deleted file mode 100644
index cbf0829b5..000000000
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.personalization;
-
-import android.content.Context;
-
-import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.ExpandableBinaryDictionary;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-
-import java.io.File;
-import java.util.Locale;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Locally gathers statistics about the words user types and various other signals like
- * auto-correction cancellation or manual picks. This allows the keyboard to adapt to the
- * typist over time.
- */
-public class UserHistoryDictionary extends ExpandableBinaryDictionary {
- static final String NAME = UserHistoryDictionary.class.getSimpleName();
-
- // TODO: Make this constructor private
- UserHistoryDictionary(final Context context, final Locale locale,
- @Nullable final String account) {
- super(context, getUserHistoryDictName(NAME, locale, null /* dictFile */, account), locale, Dictionary.TYPE_USER_HISTORY, null);
- if (mLocale != null && mLocale.toString().length() > 1) {
- reloadDictionaryIfRequired();
- }
- }
-
- /**
- * @returns the name of the {@link UserHistoryDictionary}.
- */
- @UsedForTesting
- static String getUserHistoryDictName(final String name, final Locale locale,
- @Nullable final File dictFile, @Nullable final String account) {
- if (!ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) {
- return getDictName(name, locale, dictFile);
- }
- return getUserHistoryDictNamePerAccount(name, locale, dictFile, account);
- }
-
- /**
- * Uses the currently signed in account to determine the dictionary name.
- */
- private static String getUserHistoryDictNamePerAccount(final String name, final Locale locale,
- @Nullable final File dictFile, @Nullable final String account) {
- if (dictFile != null) {
- return dictFile.getName();
- }
- String dictName = name + "." + locale.toString();
- if (account != null) {
- dictName += "." + account;
- }
- return dictName;
- }
-
- // Note: This method is called by {@link DictionaryFacilitator} using Java reflection.
- @SuppressWarnings("unused")
- @ExternallyReferenced
- public static UserHistoryDictionary getDictionary(final Context context, final Locale locale,
- final File dictFile, final String dictNamePrefix, @Nullable final String account) {
- return PersonalizationHelper.getUserHistoryDictionary(context, locale, account);
- }
-
- /**
- * Add a word to the user history dictionary.
- *
- * @param userHistoryDictionary the user history dictionary
- * @param ngramContext the n-gram context
- * @param word the word the user inputted
- * @param isValid whether the word is valid or not
- * @param timestamp the timestamp when the word has been inputted
- */
- public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary,
- @Nonnull final NgramContext ngramContext, final String word, final boolean isValid,
- final int timestamp) {
- if (word.length() > BinaryDictionary.DICTIONARY_MAX_WORD_LENGTH) {
- return;
- }
- userHistoryDictionary.updateEntriesForWord(ngramContext, word,
- isValid, 1 /* count */, timestamp);
- }
-
- @Override
- public void close() {
- // Flush pending writes.
- asyncFlushBinaryDictionary();
- super.close();
- }
-
- @Override
- protected Map<String, String> getHeaderAttributeMap() {
- final Map<String, String> attributeMap = super.getHeaderAttributeMap();
- attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
- DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
- DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
- return attributeMap;
- }
-
- @Override
- protected void loadInitialContentsLocked() {
- // No initial contents.
- }
-
- @Override
- public boolean isValidWord(final String word) {
- // Strings out of this dictionary should not be considered existing words.
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
deleted file mode 100644
index b39e6b477..000000000
--- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ACCOUNT_NAME;
-import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ENABLE_CLOUD_SYNC;
-
-import android.Manifest;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnShowListener;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.TwoStatePreference;
-import android.text.TextUtils;
-import android.text.method.LinkMovementMethod;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.accounts.AccountStateChangedListener;
-import com.android.inputmethod.latin.accounts.LoginAccountUtils;
-import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.utils.ManagedProfileUtils;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.annotation.Nullable;
-
-/**
- * "Accounts & Privacy" settings sub screen.
- *
- * This settings sub screen handles the following preferences:
- * <li> Account selection/management for IME </li>
- * <li> Sync preferences </li>
- * <li> Privacy preferences </li>
- */
-public final class AccountsSettingsFragment extends SubScreenFragment {
- private static final String PREF_ENABLE_SYNC_NOW = "pref_enable_cloud_sync";
- private static final String PREF_SYNC_NOW = "pref_sync_now";
- private static final String PREF_CLEAR_SYNC_DATA = "pref_clear_sync_data";
-
- static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
-
- /**
- * Onclick listener for sync now pref.
- */
- private final Preference.OnPreferenceClickListener mSyncNowListener =
- new SyncNowListener();
- /**
- * Onclick listener for delete sync pref.
- */
- private final Preference.OnPreferenceClickListener mDeleteSyncDataListener =
- new DeleteSyncDataListener();
-
- /**
- * Onclick listener for enable sync pref.
- */
- private final Preference.OnPreferenceClickListener mEnableSyncClickListener =
- new EnableSyncClickListener();
-
- /**
- * Enable sync checkbox pref.
- */
- private TwoStatePreference mEnableSyncPreference;
-
- /**
- * Enable sync checkbox pref.
- */
- private Preference mSyncNowPreference;
-
- /**
- * Clear sync data pref.
- */
- private Preference mClearSyncDataPreference;
-
- /**
- * Account switcher preference.
- */
- private Preference mAccountSwitcher;
-
- /**
- * Stores if we are currently detecting a managed profile.
- */
- private AtomicBoolean mManagedProfileBeingDetected = new AtomicBoolean(true);
-
- /**
- * Stores if we have successfully detected if the device has a managed profile.
- */
- private AtomicBoolean mHasManagedProfile = new AtomicBoolean(false);
-
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_accounts);
-
- mAccountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
- mEnableSyncPreference = (TwoStatePreference) findPreference(PREF_ENABLE_SYNC_NOW);
- mSyncNowPreference = findPreference(PREF_SYNC_NOW);
- mClearSyncDataPreference = findPreference(PREF_CLEAR_SYNC_DATA);
-
- if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) {
- final Preference enableMetricsLogging =
- findPreference(Settings.PREF_ENABLE_METRICS_LOGGING);
- final Resources res = getResources();
- if (enableMetricsLogging != null) {
- final String enableMetricsLoggingTitle = res.getString(
- R.string.enable_metrics_logging, getApplicationName());
- enableMetricsLogging.setTitle(enableMetricsLoggingTitle);
- }
- } else {
- removePreference(Settings.PREF_ENABLE_METRICS_LOGGING);
- }
-
- if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
- removeSyncPreferences();
- } else {
- // Disable by default till we are sure we can enable this.
- disableSyncPreferences();
- new ManagedProfileCheckerTask(this).execute();
- }
- }
-
- /**
- * Task to check work profile. If found, it removes the sync prefs. If not,
- * it enables them.
- */
- private static class ManagedProfileCheckerTask extends AsyncTask<Void, Void, Boolean> {
- private final AccountsSettingsFragment mFragment;
-
- private ManagedProfileCheckerTask(final AccountsSettingsFragment fragment) {
- mFragment = fragment;
- }
-
- @Override
- protected void onPreExecute() {
- mFragment.mManagedProfileBeingDetected.set(true);
- }
- @Override
- protected Boolean doInBackground(Void... params) {
- return ManagedProfileUtils.getInstance().hasWorkProfile(mFragment.getActivity());
- }
-
- @Override
- protected void onPostExecute(final Boolean hasWorkProfile) {
- mFragment.mHasManagedProfile.set(hasWorkProfile);
- mFragment.mManagedProfileBeingDetected.set(false);
- mFragment.refreshSyncSettingsUI();
- }
- }
-
- private void enableSyncPreferences(final String[] accountsForLogin,
- final String currentAccountName) {
- if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
- return;
- }
- mAccountSwitcher.setEnabled(true);
-
- mEnableSyncPreference.setEnabled(true);
- mEnableSyncPreference.setOnPreferenceClickListener(mEnableSyncClickListener);
-
- mSyncNowPreference.setEnabled(true);
- mSyncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
-
- mClearSyncDataPreference.setEnabled(true);
- mClearSyncDataPreference.setOnPreferenceClickListener(mDeleteSyncDataListener);
-
- if (currentAccountName != null) {
- mAccountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(final Preference preference) {
- if (accountsForLogin.length > 0) {
- // TODO: Add addition of account.
- createAccountPicker(accountsForLogin, getSignedInAccountName(),
- new AccountChangedListener(null)).show();
- }
- return true;
- }
- });
- }
- }
-
- /**
- * Two reasons for disable - work profile or no accounts on device.
- */
- private void disableSyncPreferences() {
- if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
- return;
- }
-
- mAccountSwitcher.setEnabled(false);
- mEnableSyncPreference.setEnabled(false);
- mSyncNowPreference.setEnabled(false);
- mClearSyncDataPreference.setEnabled(false);
- }
-
- /**
- * Called only when ProductionFlag is turned off.
- */
- private void removeSyncPreferences() {
- removePreference(PREF_ACCCOUNT_SWITCHER);
- removePreference(PREF_ENABLE_CLOUD_SYNC);
- removePreference(PREF_SYNC_NOW);
- removePreference(PREF_CLEAR_SYNC_DATA);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- refreshSyncSettingsUI();
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- if (TextUtils.equals(key, PREF_ACCOUNT_NAME)) {
- refreshSyncSettingsUI();
- } else if (TextUtils.equals(key, PREF_ENABLE_CLOUD_SYNC)) {
- mEnableSyncPreference = (TwoStatePreference) findPreference(PREF_ENABLE_SYNC_NOW);
- final boolean syncEnabled = prefs.getBoolean(PREF_ENABLE_CLOUD_SYNC, false);
- if (isSyncEnabled()) {
- mEnableSyncPreference.setSummary(getString(R.string.cloud_sync_summary));
- } else {
- mEnableSyncPreference.setSummary(getString(R.string.cloud_sync_summary_disabled));
- }
- AccountStateChangedListener.onSyncPreferenceChanged(getSignedInAccountName(),
- syncEnabled);
- }
- }
-
- /**
- * Checks different states like whether account is present or managed profile is present
- * and sets the sync settings accordingly.
- */
- private void refreshSyncSettingsUI() {
- if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
- return;
- }
- boolean hasAccountsPermission = PermissionsUtil.checkAllPermissionsGranted(
- getActivity(), Manifest.permission.READ_CONTACTS);
-
- final String[] accountsForLogin = hasAccountsPermission ?
- LoginAccountUtils.getAccountsForLogin(getActivity()) : new String[0];
- final String currentAccount = hasAccountsPermission ? getSignedInAccountName() : null;
-
- if (hasAccountsPermission && !mManagedProfileBeingDetected.get() &&
- !mHasManagedProfile.get() && accountsForLogin.length > 0) {
- // Sync can be used by user; enable all preferences.
- enableSyncPreferences(accountsForLogin, currentAccount);
- } else {
- // Sync cannot be used by user; disable all preferences.
- disableSyncPreferences();
- }
- refreshSyncSettingsMessaging(hasAccountsPermission, mManagedProfileBeingDetected.get(),
- mHasManagedProfile.get(), accountsForLogin.length > 0,
- currentAccount);
- }
-
- /**
- * @param hasAccountsPermission whether the app has the permission to read accounts.
- * @param managedProfileBeingDetected whether we are in process of determining work profile.
- * @param hasManagedProfile whether the device has work profile.
- * @param hasAccountsForLogin whether the device has enough accounts for login.
- * @param currentAccount the account currently selected in the application.
- */
- private void refreshSyncSettingsMessaging(boolean hasAccountsPermission,
- boolean managedProfileBeingDetected,
- boolean hasManagedProfile,
- boolean hasAccountsForLogin,
- String currentAccount) {
- if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
- return;
- }
-
- if (!hasAccountsPermission) {
- mEnableSyncPreference.setChecked(false);
- mEnableSyncPreference.setSummary(getString(R.string.cloud_sync_summary_disabled));
- mAccountSwitcher.setSummary("");
- return;
- } else if (managedProfileBeingDetected) {
- // If we are determining eligiblity, we show empty summaries.
- // Once we have some deterministic result, we set summaries based on different results.
- mEnableSyncPreference.setSummary("");
- mAccountSwitcher.setSummary("");
- } else if (hasManagedProfile) {
- mEnableSyncPreference.setSummary(
- getString(R.string.cloud_sync_summary_disabled_work_profile));
- } else if (!hasAccountsForLogin) {
- mEnableSyncPreference.setSummary(getString(R.string.add_account_to_enable_sync));
- } else if (isSyncEnabled()) {
- mEnableSyncPreference.setSummary(getString(R.string.cloud_sync_summary));
- } else {
- mEnableSyncPreference.setSummary(getString(R.string.cloud_sync_summary_disabled));
- }
-
- // Set some interdependent settings.
- // No account automatically turns off sync.
- if (!managedProfileBeingDetected && !hasManagedProfile) {
- if (currentAccount != null) {
- mAccountSwitcher.setSummary(getString(R.string.account_selected, currentAccount));
- } else {
- mEnableSyncPreference.setChecked(false);
- mAccountSwitcher.setSummary(getString(R.string.no_accounts_selected));
- }
- }
- }
-
- @Nullable
- String getSignedInAccountName() {
- return getSharedPreferences().getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
- }
-
- boolean isSyncEnabled() {
- return getSharedPreferences().getBoolean(PREF_ENABLE_CLOUD_SYNC, false);
- }
-
- /**
- * Creates an account picker dialog showing the given accounts in a list and selecting
- * the selected account by default. The list of accounts must not be null/empty.
- *
- * Package-private for testing.
- *
- * @param accounts list of accounts on the device.
- * @param selectedAccount currently selected account
- * @param positiveButtonClickListener listener that gets called when positive button is
- * clicked
- */
- @UsedForTesting
- AlertDialog createAccountPicker(final String[] accounts,
- final String selectedAccount,
- final DialogInterface.OnClickListener positiveButtonClickListener) {
- if (accounts == null || accounts.length == 0) {
- throw new IllegalArgumentException("List of accounts must not be empty");
- }
-
- // See if the currently selected account is in the list.
- // If it is, the entry is selected, and a sign-out button is provided.
- // If it isn't, select the 0th account by default which will get picked up
- // if the user presses OK.
- int index = 0;
- boolean isSignedIn = false;
- for (int i = 0; i < accounts.length; i++) {
- if (TextUtils.equals(accounts[i], selectedAccount)) {
- index = i;
- isSignedIn = true;
- break;
- }
- }
- final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.account_select_title)
- .setSingleChoiceItems(accounts, index, null)
- .setPositiveButton(R.string.account_select_ok, positiveButtonClickListener)
- .setNegativeButton(R.string.account_select_cancel, null);
- if (isSignedIn) {
- builder.setNeutralButton(R.string.account_select_sign_out, positiveButtonClickListener);
- }
- return builder.create();
- }
-
- /**
- * Listener for a account selection changes from the picker.
- * Persists/removes the account to/from shared preferences and sets up sync if required.
- */
- class AccountChangedListener implements DialogInterface.OnClickListener {
- /**
- * Represents preference that should be changed based on account chosen.
- */
- private TwoStatePreference mDependentPreference;
-
- AccountChangedListener(final TwoStatePreference dependentPreference) {
- mDependentPreference = dependentPreference;
- }
-
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- final String oldAccount = getSignedInAccountName();
- switch (which) {
- case DialogInterface.BUTTON_POSITIVE: // Signed in
- final ListView lv = ((AlertDialog)dialog).getListView();
- final String newAccount =
- (String) lv.getItemAtPosition(lv.getCheckedItemPosition());
- getSharedPreferences()
- .edit()
- .putString(PREF_ACCOUNT_NAME, newAccount)
- .apply();
- AccountStateChangedListener.onAccountSignedIn(oldAccount, newAccount);
- if (mDependentPreference != null) {
- mDependentPreference.setChecked(true);
- }
- break;
- case DialogInterface.BUTTON_NEUTRAL: // Signed out
- AccountStateChangedListener.onAccountSignedOut(oldAccount);
- getSharedPreferences()
- .edit()
- .remove(PREF_ACCOUNT_NAME)
- .apply();
- break;
- }
- }
- }
-
- /**
- * Listener that initiates the process of sync in the background.
- */
- class SyncNowListener implements Preference.OnPreferenceClickListener {
- @Override
- public boolean onPreferenceClick(final Preference preference) {
- AccountStateChangedListener.forceSync(getSignedInAccountName());
- return true;
- }
- }
-
- /**
- * Listener that initiates the process of deleting user's data from the cloud.
- */
- class DeleteSyncDataListener implements Preference.OnPreferenceClickListener {
- @Override
- public boolean onPreferenceClick(final Preference preference) {
- final AlertDialog confirmationDialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.clear_sync_data_title)
- .setMessage(R.string.clear_sync_data_confirmation)
- .setPositiveButton(R.string.clear_sync_data_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- AccountStateChangedListener.forceDelete(
- getSignedInAccountName());
- }
- }
- })
- .setNegativeButton(R.string.cloud_sync_cancel, null /* OnClickListener */)
- .create();
- confirmationDialog.show();
- return true;
- }
- }
-
- /**
- * Listens to events when user clicks on "Enable sync" feature.
- */
- class EnableSyncClickListener implements OnShowListener, Preference.OnPreferenceClickListener {
- // TODO(cvnguyen): Write tests.
- @Override
- public boolean onPreferenceClick(final Preference preference) {
- final TwoStatePreference syncPreference = (TwoStatePreference) preference;
- if (syncPreference.isChecked()) {
- // Uncheck for now.
- syncPreference.setChecked(false);
-
- // Show opt-in.
- final AlertDialog optInDialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.cloud_sync_title)
- .setMessage(R.string.cloud_sync_opt_in_text)
- .setPositiveButton(R.string.account_select_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog,
- final int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- final Context context = getActivity();
- final String[] accountsForLogin =
- LoginAccountUtils.getAccountsForLogin(context);
- createAccountPicker(accountsForLogin,
- getSignedInAccountName(),
- new AccountChangedListener(syncPreference))
- .show();
- }
- }
- })
- .setNegativeButton(R.string.cloud_sync_cancel, null)
- .create();
- optInDialog.setOnShowListener(this);
- optInDialog.show();
- }
- return true;
- }
-
- @Override
- public void onShow(DialogInterface dialog) {
- TextView messageView = (TextView) ((AlertDialog) dialog).findViewById(
- android.R.id.message);
- if (messageView != null) {
- messageView.setMovementMethod(LinkMovementMethod.getInstance());
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
deleted file mode 100644
index 4e8a10b1f..000000000
--- a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.PreferenceFragment;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.RichInputMethodManager;
-
-import javax.annotation.Nonnull;
-
-/**
- * Utility class for managing additional features settings.
- */
-@SuppressWarnings("unused")
-public class AdditionalFeaturesSettingUtils {
- public static final int ADDITIONAL_FEATURES_SETTINGS_SIZE = 0;
-
- private AdditionalFeaturesSettingUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static void addAdditionalFeaturesPreferences(
- final Context context, final PreferenceFragment settingsFragment) {
- // do nothing.
- }
-
- public static void readAdditionalFeaturesPreferencesIntoArray(final Context context,
- final SharedPreferences prefs, final int[] additionalFeaturesPreferences) {
- // do nothing.
- }
-
- @Nonnull
- public static RichInputMethodSubtype createRichInputMethodSubtype(
- @Nonnull final RichInputMethodManager imm,
- @Nonnull final InputMethodSubtype subtype,
- final Context context) {
- return new RichInputMethodSubtype(subtype);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
deleted file mode 100644
index a6fb7f1f1..000000000
--- a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.media.AudioManager;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-
-import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SystemBroadcastReceiver;
-import com.android.inputmethod.latin.define.ProductionFlags;
-
-/**
- * "Advanced" settings sub screen.
- *
- * This settings sub screen handles the following advanced preferences.
- * - Key popup dismiss delay
- * - Keypress vibration duration
- * - Keypress sound volume
- * - Show app icon
- * - Improve keyboard
- * - Debug settings
- */
-public final class AdvancedSettingsFragment extends SubScreenFragment {
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_advanced);
-
- final Resources res = getResources();
- final Context context = getActivity();
-
- // When we are called from the Settings application but we are not already running, some
- // singleton and utility classes may not have been initialized. We have to call
- // initialization method of these classes here. See {@link LatinIME#onCreate()}.
- AudioAndHapticFeedbackManager.init(context);
-
- final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
-
- if (!Settings.isInternal(prefs)) {
- removePreference(Settings.SCREEN_DEBUG);
- }
-
- if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) {
- removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS);
- }
-
- // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
- if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
- removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- } else {
- // TODO: Cleanup this setup.
- final ListPreference keyPreviewPopupDismissDelay =
- (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
- R.integer.config_key_preview_linger_timeout));
- keyPreviewPopupDismissDelay.setEntries(new String[] {
- res.getString(R.string.key_preview_popup_dismiss_no_delay),
- res.getString(R.string.key_preview_popup_dismiss_default_delay),
- });
- keyPreviewPopupDismissDelay.setEntryValues(new String[] {
- "0",
- popupDismissDelayDefaultValue
- });
- if (null == keyPreviewPopupDismissDelay.getValue()) {
- keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
- }
- keyPreviewPopupDismissDelay.setEnabled(
- Settings.readKeyPreviewPopupEnabled(prefs, res));
- }
-
- setupKeypressVibrationDurationSettings();
- setupKeypressSoundVolumeSettings();
- setupKeyLongpressTimeoutSettings();
- refreshEnablingsOfKeypressSoundAndVibrationSettings();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- final Resources res = getResources();
- if (key.equals(Settings.PREF_POPUP_ON)) {
- setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
- Settings.readKeyPreviewPopupEnabled(prefs, res));
- } else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) {
- SystemBroadcastReceiver.toggleAppIcon(getActivity());
- }
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- refreshEnablingsOfKeypressSoundAndVibrationSettings();
- }
-
- private void refreshEnablingsOfKeypressSoundAndVibrationSettings() {
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS,
- Settings.readVibrationEnabled(prefs, res));
- setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME,
- Settings.readKeypressSoundEnabled(prefs, res));
- }
-
- private void setupKeypressVibrationDurationSettings() {
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
- Settings.PREF_VIBRATION_DURATION_SETTINGS);
- if (pref == null) {
- return;
- }
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putInt(key, value).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return Settings.readKeypressVibrationDuration(prefs, res);
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return Settings.readDefaultKeypressVibrationDuration(res);
- }
-
- @Override
- public void feedbackValue(final int value) {
- AudioAndHapticFeedbackManager.getInstance().vibrate(value);
- }
-
- @Override
- public String getValueText(final int value) {
- if (value < 0) {
- return res.getString(R.string.settings_system_default);
- }
- return res.getString(R.string.abbreviation_unit_milliseconds, value);
- }
- });
- }
-
- private void setupKeypressSoundVolumeSettings() {
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
- Settings.PREF_KEYPRESS_SOUND_VOLUME);
- if (pref == null) {
- return;
- }
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- final AudioManager am = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE);
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- private static final float PERCENTAGE_FLOAT = 100.0f;
-
- private float getValueFromPercentage(final int percentage) {
- return percentage / PERCENTAGE_FLOAT;
- }
-
- private int getPercentageFromValue(final float floatValue) {
- return (int)(floatValue * PERCENTAGE_FLOAT);
- }
-
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putFloat(key, getValueFromPercentage(value)).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return getPercentageFromValue(Settings.readKeypressSoundVolume(prefs, res));
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res));
- }
-
- @Override
- public String getValueText(final int value) {
- if (value < 0) {
- return res.getString(R.string.settings_system_default);
- }
- return Integer.toString(value);
- }
-
- @Override
- public void feedbackValue(final int value) {
- am.playSoundEffect(
- AudioManager.FX_KEYPRESS_STANDARD, getValueFromPercentage(value));
- }
- });
- }
-
- private void setupKeyLongpressTimeoutSettings() {
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
- Settings.PREF_KEY_LONGPRESS_TIMEOUT);
- if (pref == null) {
- return;
- }
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putInt(key, value).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return Settings.readKeyLongpressTimeout(prefs, res);
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return Settings.readDefaultKeyLongpressTimeout(res);
- }
-
- @Override
- public String getValueText(final int value) {
- return res.getString(R.string.abbreviation_unit_milliseconds, value);
- }
-
- @Override
- public void feedbackValue(final int value) {}
- });
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
deleted file mode 100644
index 554edc85c..000000000
--- a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.os.Bundle;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.define.ProductionFlags;
-
-/**
- * "Appearance" settings sub screen.
- */
-public final class AppearanceSettingsFragment extends SubScreenFragment {
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_appearance);
- if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED ||
- Constants.isPhone(Settings.readScreenMetrics(getResources()))) {
- removePreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- CustomInputStyleSettingsFragment.updateCustomInputStylesSummary(
- findPreference(Settings.PREF_CUSTOM_INPUT_STYLES));
- ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME));
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java
deleted file mode 100644
index dfe899ece..000000000
--- a/java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.SwitchPreference;
-import android.text.TextUtils;
-
-import com.android.inputmethod.dictionarypack.DictionarySettingsActivity;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.permissions.PermissionsManager;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.userdictionary.UserDictionaryList;
-import com.android.inputmethod.latin.userdictionary.UserDictionarySettings;
-
-import java.util.TreeSet;
-
-/**
- * "Text correction" settings sub screen.
- *
- * This settings sub screen handles the following text correction preferences.
- * - Personal dictionary
- * - Add-on dictionaries
- * - Block offensive words
- * - Auto-correction
- * - Show correction suggestions
- * - Personalized suggestions
- * - Suggest Contact names
- * - Next-word suggestions
- */
-public final class CorrectionSettingsFragment extends SubScreenFragment
- implements SharedPreferences.OnSharedPreferenceChangeListener,
- PermissionsManager.PermissionsResultCallback {
-
- private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false;
- private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS =
- DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS
- || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2;
-
- private SwitchPreference mUseContactsPreference;
-
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_correction);
-
- final Context context = getActivity();
- final PackageManager pm = context.getPackageManager();
-
- final Preference dictionaryLink = findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY);
- final Intent intent = dictionaryLink.getIntent();
- intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName());
- final int number = pm.queryIntentActivities(intent, 0).size();
- if (0 >= number) {
- removePreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY);
- }
-
- final Preference editPersonalDictionary =
- findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY);
- final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent();
- final ResolveInfo ri = USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS ? null
- : pm.resolveActivity(
- editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY);
- if (ri == null) {
- overwriteUserDictionaryPreference(editPersonalDictionary);
- }
-
- mUseContactsPreference = (SwitchPreference) findPreference(Settings.PREF_KEY_USE_CONTACTS_DICT);
- turnOffUseContactsIfNoPermission();
- }
-
- private void overwriteUserDictionaryPreference(final Preference userDictionaryPreference) {
- final Activity activity = getActivity();
- final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity);
- if (null == localeList) {
- // The locale list is null if and only if the user dictionary service is
- // not present or disabled. In this case we need to remove the preference.
- getPreferenceScreen().removePreference(userDictionaryPreference);
- } else if (localeList.size() <= 1) {
- userDictionaryPreference.setFragment(UserDictionarySettings.class.getName());
- // If the size of localeList is 0, we don't set the locale parameter in the
- // extras. This will be interpreted by the UserDictionarySettings class as
- // meaning "the current locale".
- // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet()
- // the locale list always has at least one element, since it always includes the current
- // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet().
- if (localeList.size() == 1) {
- final String locale = (String)localeList.toArray()[0];
- userDictionaryPreference.getExtras().putString("locale", locale);
- }
- } else {
- userDictionaryPreference.setFragment(UserDictionaryList.class.getName());
- }
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
- if (!TextUtils.equals(key, Settings.PREF_KEY_USE_CONTACTS_DICT)) {
- return;
- }
- if (!sharedPreferences.getBoolean(key, false)) {
- // don't care if the preference is turned off.
- return;
- }
-
- // Check for permissions.
- if (PermissionsUtil.checkAllPermissionsGranted(
- getActivity() /* context */, Manifest.permission.READ_CONTACTS)) {
- return; // all permissions granted, no need to request permissions.
- }
-
- PermissionsManager.get(getActivity() /* context */).requestPermissions(
- this /* PermissionsResultCallback */,
- getActivity() /* activity */,
- Manifest.permission.READ_CONTACTS);
- }
-
- @Override
- public void onRequestPermissionsResult(boolean allGranted) {
- turnOffUseContactsIfNoPermission();
- }
-
- private void turnOffUseContactsIfNoPermission() {
- if (!PermissionsUtil.checkAllPermissionsGranted(
- getActivity(), Manifest.permission.READ_CONTACTS)) {
- mUseContactsPreference.setChecked(false);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
deleted file mode 100644
index 44a98c1be..000000000
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.preference.DialogPreference;
-import android.preference.Preference;
-import android.util.Log;
-import android.view.View;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.SpinnerAdapter;
-
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.compat.ViewCompatUtils;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-import java.util.TreeSet;
-
-final class CustomInputStylePreference extends DialogPreference
- implements DialogInterface.OnCancelListener {
- private static final boolean DEBUG_SUBTYPE_ID = false;
-
- interface Listener {
- public void onRemoveCustomInputStyle(CustomInputStylePreference stylePref);
- public void onSaveCustomInputStyle(CustomInputStylePreference stylePref);
- public void onAddCustomInputStyle(CustomInputStylePreference stylePref);
- public SubtypeLocaleAdapter getSubtypeLocaleAdapter();
- public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter();
- }
-
- private static final String KEY_PREFIX = "subtype_pref_";
- private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new";
-
- private InputMethodSubtype mSubtype;
- private InputMethodSubtype mPreviousSubtype;
-
- private final Listener mProxy;
- private Spinner mSubtypeLocaleSpinner;
- private Spinner mKeyboardLayoutSetSpinner;
-
- public static CustomInputStylePreference newIncompleteSubtypePreference(
- final Context context, final Listener proxy) {
- return new CustomInputStylePreference(context, null, proxy);
- }
-
- public CustomInputStylePreference(final Context context, final InputMethodSubtype subtype,
- final Listener proxy) {
- super(context, null);
- setDialogLayoutResource(R.layout.additional_subtype_dialog);
- setPersistent(false);
- mProxy = proxy;
- setSubtype(subtype);
- }
-
- public void show() {
- showDialog(null);
- }
-
- public final boolean isIncomplete() {
- return mSubtype == null;
- }
-
- public InputMethodSubtype getSubtype() {
- return mSubtype;
- }
-
- public void setSubtype(final InputMethodSubtype subtype) {
- mPreviousSubtype = mSubtype;
- mSubtype = subtype;
- if (isIncomplete()) {
- setTitle(null);
- setDialogTitle(R.string.add_style);
- setKey(KEY_NEW_SUBTYPE);
- } else {
- final String displayName =
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
- setTitle(displayName);
- setDialogTitle(displayName);
- setKey(KEY_PREFIX + subtype.getLocale() + "_"
- + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype));
- }
- }
-
- public void revert() {
- setSubtype(mPreviousSubtype);
- }
-
- public boolean hasBeenModified() {
- return mSubtype != null && !mSubtype.equals(mPreviousSubtype);
- }
-
- @Override
- protected View onCreateDialogView() {
- final View v = super.onCreateDialogView();
- mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner);
- mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter());
- mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner);
- mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter());
- // All keyboard layout names are in the Latin script and thus left to right. That means
- // the view would align them to the left even if the system locale is RTL, but that
- // would look strange. To fix this, we align them to the view's start, which will be
- // natural for any direction.
- ViewCompatUtils.setTextAlignment(
- mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START);
- return v;
- }
-
- @Override
- protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
- builder.setCancelable(true).setOnCancelListener(this);
- if (isIncomplete()) {
- builder.setPositiveButton(R.string.add, this)
- .setNegativeButton(android.R.string.cancel, this);
- } else {
- builder.setPositiveButton(R.string.save, this)
- .setNeutralButton(android.R.string.cancel, this)
- .setNegativeButton(R.string.remove, this);
- final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype);
- final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype);
- setSpinnerPosition(mSubtypeLocaleSpinner, localeItem);
- setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem);
- }
- }
-
- private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) {
- final SpinnerAdapter adapter = spinner.getAdapter();
- final int count = adapter.getCount();
- for (int i = 0; i < count; i++) {
- final Object item = spinner.getItemAtPosition(i);
- if (item.equals(itemToSelect)) {
- spinner.setSelection(i);
- return;
- }
- }
- }
-
- @Override
- public void onCancel(final DialogInterface dialog) {
- if (isIncomplete()) {
- mProxy.onRemoveCustomInputStyle(this);
- }
- }
-
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- super.onClick(dialog, which);
- switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
- final boolean isEditing = !isIncomplete();
- final SubtypeLocaleItem locale =
- (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem();
- final KeyboardLayoutSetItem layout =
- (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
- final InputMethodSubtype subtype =
- AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
- locale.mLocaleString, layout.mLayoutName);
- setSubtype(subtype);
- notifyChanged();
- if (isEditing) {
- mProxy.onSaveCustomInputStyle(this);
- } else {
- mProxy.onAddCustomInputStyle(this);
- }
- break;
- case DialogInterface.BUTTON_NEUTRAL:
- // Nothing to do
- break;
- case DialogInterface.BUTTON_NEGATIVE:
- mProxy.onRemoveCustomInputStyle(this);
- break;
- }
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- final Parcelable superState = super.onSaveInstanceState();
- final Dialog dialog = getDialog();
- if (dialog == null || !dialog.isShowing()) {
- return superState;
- }
-
- final SavedState myState = new SavedState(superState);
- myState.mSubtype = mSubtype;
- return myState;
- }
-
- @Override
- protected void onRestoreInstanceState(final Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
-
- final SavedState myState = (SavedState) state;
- super.onRestoreInstanceState(myState.getSuperState());
- setSubtype(myState.mSubtype);
- }
-
- static final class SavedState extends Preference.BaseSavedState {
- InputMethodSubtype mSubtype;
-
- public SavedState(final Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- super.writeToParcel(dest, flags);
- dest.writeParcelable(mSubtype, 0);
- }
-
- public SavedState(final Parcel source) {
- super(source);
- mSubtype = (InputMethodSubtype)source.readParcelable(null);
- }
-
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<SavedState> CREATOR =
- new Parcelable.Creator<SavedState>() {
- @Override
- public SavedState createFromParcel(final Parcel source) {
- return new SavedState(source);
- }
-
- @Override
- public SavedState[] newArray(final int size) {
- return new SavedState[size];
- }
- };
- }
-
- static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> {
- public final String mLocaleString;
- private final String mDisplayName;
-
- public SubtypeLocaleItem(final InputMethodSubtype subtype) {
- mLocaleString = subtype.getLocale();
- mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(
- mLocaleString);
- }
-
- // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
- // to get display name.
- @Override
- public String toString() {
- return mDisplayName;
- }
-
- @Override
- public int compareTo(final SubtypeLocaleItem o) {
- return mLocaleString.compareTo(o.mLocaleString);
- }
- }
-
- static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> {
- private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName();
-
- public SubtypeLocaleAdapter(final Context context) {
- super(context, android.R.layout.simple_spinner_item);
- setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-
- final TreeSet<SubtypeLocaleItem> items = new TreeSet<>();
- final InputMethodInfo imi = RichInputMethodManager.getInstance()
- .getInputMethodInfoOfThisIme();
- final int count = imi.getSubtypeCount();
- for (int i = 0; i < count; i++) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(i);
- if (DEBUG_SUBTYPE_ID) {
- Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s",
- subtype.getLocale(), subtype.hashCode(), subtype.hashCode(),
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)));
- }
- if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
- items.add(new SubtypeLocaleItem(subtype));
- }
- }
- // TODO: Should filter out already existing combinations of locale and layout.
- addAll(items);
- }
- }
-
- static final class KeyboardLayoutSetItem {
- public final String mLayoutName;
- private final String mDisplayName;
-
- public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
- mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
- mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype);
- }
-
- // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
- // to get display name.
- @Override
- public String toString() {
- return mDisplayName;
- }
- }
-
- static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
- public KeyboardLayoutSetAdapter(final Context context) {
- super(context, android.R.layout.simple_spinner_item);
- setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-
- final String[] predefinedKeyboardLayoutSet = context.getResources().getStringArray(
- R.array.predefined_layouts);
- // TODO: Should filter out already existing combinations of locale and layout.
- for (final String layout : predefinedKeyboardLayoutSet) {
- // This is a placeholder for a subtype with NO_LANGUAGE, only for display.
- final InputMethodSubtype subtype =
- AdditionalSubtypeUtils.createDummyAdditionalSubtype(
- SubtypeLocaleUtils.NO_LANGUAGE, layout);
- add(new KeyboardLayoutSetItem(subtype));
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
deleted file mode 100644
index 56e8f1623..000000000
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceGroup;
-import androidx.core.view.ViewCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Toast;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.DialogUtils;
-import com.android.inputmethod.latin.utils.IntentUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-
-import java.util.ArrayList;
-
-public final class CustomInputStyleSettingsFragment extends PreferenceFragment
- implements CustomInputStylePreference.Listener {
- private static final String TAG = CustomInputStyleSettingsFragment.class.getSimpleName();
- // Note: We would like to turn this debug flag true in order to see what input styles are
- // defined in a bug-report.
- private static final boolean DEBUG_CUSTOM_INPUT_STYLES = true;
-
- private RichInputMethodManager mRichImm;
- private SharedPreferences mPrefs;
- private CustomInputStylePreference.SubtypeLocaleAdapter mSubtypeLocaleAdapter;
- private CustomInputStylePreference.KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
-
- private boolean mIsAddingNewSubtype;
- private AlertDialog mSubtypeEnablerNotificationDialog;
- private String mSubtypePreferenceKeyForSubtypeEnabler;
-
- private static final String KEY_IS_ADDING_NEW_SUBTYPE = "is_adding_new_subtype";
- private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN =
- "is_subtype_enabler_notification_dialog_open";
- private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
-
- public CustomInputStyleSettingsFragment() {
- // Empty constructor for fragment generation.
- }
-
- static void updateCustomInputStylesSummary(final Preference pref) {
- // When we are called from the Settings application but we are not already running, some
- // singleton and utility classes may not have been initialized. We have to call
- // initialization method of these classes here. See {@link LatinIME#onCreate()}.
- SubtypeLocaleUtils.init(pref.getContext());
-
- final Resources res = pref.getContext().getResources();
- final SharedPreferences prefs = pref.getSharedPreferences();
- final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
- final InputMethodSubtype[] subtypes =
- AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
- final ArrayList<String> subtypeNames = new ArrayList<>();
- for (final InputMethodSubtype subtype : subtypes) {
- subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
- }
- // TODO: A delimiter of custom input styles should be localized.
- pref.setSummary(TextUtils.join(", ", subtypeNames));
- }
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mPrefs = getPreferenceManager().getSharedPreferences();
- RichInputMethodManager.init(getActivity());
- mRichImm = RichInputMethodManager.getInstance();
- addPreferencesFromResource(R.xml.additional_subtype_settings);
- setHasOptionsMenu(true);
- }
-
- @Override
- public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
- final Bundle savedInstanceState) {
- final View view = super.onCreateView(inflater, container, savedInstanceState);
- // For correct display in RTL locales, we need to set the layout direction of the
- // fragment's top view.
- ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE);
- return view;
- }
-
- @Override
- public void onActivityCreated(final Bundle savedInstanceState) {
- final Context context = getActivity();
- mSubtypeLocaleAdapter = new CustomInputStylePreference.SubtypeLocaleAdapter(context);
- mKeyboardLayoutSetAdapter =
- new CustomInputStylePreference.KeyboardLayoutSetAdapter(context);
-
- final String prefSubtypes =
- Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
- if (DEBUG_CUSTOM_INPUT_STYLES) {
- Log.i(TAG, "Load custom input styles: " + prefSubtypes);
- }
- setPrefSubtypes(prefSubtypes, context);
-
- mIsAddingNewSubtype = (savedInstanceState != null)
- && savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE);
- if (mIsAddingNewSubtype) {
- getPreferenceScreen().addPreference(
- CustomInputStylePreference.newIncompleteSubtypePreference(context, this));
- }
-
- super.onActivityCreated(savedInstanceState);
-
- if (savedInstanceState != null && savedInstanceState.containsKey(
- KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN)) {
- mSubtypePreferenceKeyForSubtypeEnabler = savedInstanceState.getString(
- KEY_SUBTYPE_FOR_SUBTYPE_ENABLER);
- mSubtypeEnablerNotificationDialog = createDialog();
- mSubtypeEnablerNotificationDialog.show();
- }
- }
-
- @Override
- public void onSaveInstanceState(final Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mIsAddingNewSubtype) {
- outState.putBoolean(KEY_IS_ADDING_NEW_SUBTYPE, true);
- }
- if (mSubtypeEnablerNotificationDialog != null
- && mSubtypeEnablerNotificationDialog.isShowing()) {
- outState.putBoolean(KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN, true);
- outState.putString(
- KEY_SUBTYPE_FOR_SUBTYPE_ENABLER, mSubtypePreferenceKeyForSubtypeEnabler);
- }
- }
-
- @Override
- public void onRemoveCustomInputStyle(final CustomInputStylePreference stylePref) {
- mIsAddingNewSubtype = false;
- final PreferenceGroup group = getPreferenceScreen();
- group.removePreference(stylePref);
- mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
- }
-
- @Override
- public void onSaveCustomInputStyle(final CustomInputStylePreference stylePref) {
- final InputMethodSubtype subtype = stylePref.getSubtype();
- if (!stylePref.hasBeenModified()) {
- return;
- }
- if (findDuplicatedSubtype(subtype) == null) {
- mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
- return;
- }
-
- // Saved subtype is duplicated.
- final PreferenceGroup group = getPreferenceScreen();
- group.removePreference(stylePref);
- stylePref.revert();
- group.addPreference(stylePref);
- showSubtypeAlreadyExistsToast(subtype);
- }
-
- @Override
- public void onAddCustomInputStyle(final CustomInputStylePreference stylePref) {
- mIsAddingNewSubtype = false;
- final InputMethodSubtype subtype = stylePref.getSubtype();
- if (findDuplicatedSubtype(subtype) == null) {
- mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
- mSubtypePreferenceKeyForSubtypeEnabler = stylePref.getKey();
- mSubtypeEnablerNotificationDialog = createDialog();
- mSubtypeEnablerNotificationDialog.show();
- return;
- }
-
- // Newly added subtype is duplicated.
- final PreferenceGroup group = getPreferenceScreen();
- group.removePreference(stylePref);
- showSubtypeAlreadyExistsToast(subtype);
- }
-
- @Override
- public CustomInputStylePreference.SubtypeLocaleAdapter getSubtypeLocaleAdapter() {
- return mSubtypeLocaleAdapter;
- }
-
- @Override
- public CustomInputStylePreference.KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() {
- return mKeyboardLayoutSetAdapter;
- }
-
- private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) {
- final Context context = getActivity();
- final Resources res = context.getResources();
- final String message = res.getString(R.string.custom_input_style_already_exists,
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
- }
-
- private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) {
- final String localeString = subtype.getLocale();
- final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
- return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- localeString, keyboardLayoutSetName);
- }
-
- private AlertDialog createDialog() {
- final String imeId = mRichImm.getInputMethodIdOfThisIme();
- final AlertDialog.Builder builder = new AlertDialog.Builder(
- DialogUtils.getPlatformDialogThemeContext(getActivity()));
- builder.setTitle(R.string.custom_input_styles_title)
- .setMessage(R.string.custom_input_style_note_message)
- .setNegativeButton(R.string.not_now, null)
- .setPositiveButton(R.string.enable, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
- imeId,
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- // TODO: Add newly adding subtype to extra value of the intent as a hint
- // for the input language selection activity.
- // intent.putExtra("newlyAddedSubtype", subtypePref.getSubtype());
- startActivity(intent);
- }
- });
-
- return builder.create();
- }
-
- private void setPrefSubtypes(final String prefSubtypes, final Context context) {
- final PreferenceGroup group = getPreferenceScreen();
- group.removeAll();
- final InputMethodSubtype[] subtypesArray =
- AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes);
- for (final InputMethodSubtype subtype : subtypesArray) {
- final CustomInputStylePreference pref =
- new CustomInputStylePreference(context, subtype, this);
- group.addPreference(pref);
- }
- }
-
- private InputMethodSubtype[] getSubtypes() {
- final PreferenceGroup group = getPreferenceScreen();
- final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
- final int count = group.getPreferenceCount();
- for (int i = 0; i < count; i++) {
- final Preference pref = group.getPreference(i);
- if (pref instanceof CustomInputStylePreference) {
- final CustomInputStylePreference subtypePref = (CustomInputStylePreference)pref;
- // We should not save newly adding subtype to preference because it is incomplete.
- if (subtypePref.isIncomplete()) continue;
- subtypes.add(subtypePref.getSubtype());
- }
- }
- return subtypes.toArray(new InputMethodSubtype[subtypes.size()]);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- final String oldSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
- final InputMethodSubtype[] subtypes = getSubtypes();
- final String prefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(subtypes);
- if (DEBUG_CUSTOM_INPUT_STYLES) {
- Log.i(TAG, "Save custom input styles: " + prefSubtypes);
- }
- if (prefSubtypes.equals(oldSubtypes)) {
- return;
- }
- Settings.writePrefAdditionalSubtypes(mPrefs, prefSubtypes);
- mRichImm.setAdditionalInputMethodSubtypes(subtypes);
- }
-
- @Override
- public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
- inflater.inflate(R.menu.add_style, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- final int itemId = item.getItemId();
- if (itemId == R.id.action_add_style) {
- final CustomInputStylePreference newSubtype =
- CustomInputStylePreference.newIncompleteSubtypePreference(getActivity(), this);
- getPreferenceScreen().addPreference(newSubtype);
- newSubtype.show();
- mIsAddingNewSubtype = true;
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
deleted file mode 100644
index 6fffb8e9d..000000000
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-/**
- * Debug settings for the application.
- *
- * Note: Even though these settings are stored in the default shared preferences file,
- * they shouldn't be restored across devices.
- * If a new key is added here, it should also be blacklisted for restore in
- * {@link LocalSettingsConstants}.
- */
-public final class DebugSettings {
- public static final String PREF_DEBUG_MODE = "debug_mode";
- public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
- public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS =
- "pref_has_custom_key_preview_animation_params";
- public static final String PREF_RESIZE_KEYBOARD = "pref_resize_keyboard";
- public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale";
- public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
- "pref_key_preview_dismiss_duration";
- public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE =
- "pref_key_preview_dismiss_end_x_scale";
- public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE =
- "pref_key_preview_dismiss_end_y_scale";
- public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION =
- "pref_key_preview_show_up_duration";
- public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE =
- "pref_key_preview_show_up_start_x_scale";
- public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE =
- "pref_key_preview_show_up_start_y_scale";
- public static final String PREF_SHOULD_SHOW_LXX_SUGGESTION_UI =
- "pref_should_show_lxx_suggestion_ui";
- public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview";
-
- private DebugSettings() {
- // This class is not publicly instantiable.
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
deleted file mode 100644
index 37855377d..000000000
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.Process;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceGroup;
-import android.preference.TwoStatePreference;
-
-import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver;
-import com.android.inputmethod.latin.DictionaryFacilitatorImpl;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.ApplicationUtils;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-
-import java.util.Locale;
-
-/**
- * "Debug mode" settings sub screen.
- *
- * This settings sub screen handles a several preference options for debugging.
- */
-public final class DebugSettingsFragment extends SubScreenFragment
- implements OnPreferenceClickListener {
- private static final String PREF_KEY_DUMP_DICTS = "pref_key_dump_dictionaries";
- private static final String PREF_KEY_DUMP_DICT_PREFIX = "pref_key_dump_dictionaries";
-
- private boolean mServiceNeedsRestart = false;
- private TwoStatePreference mDebugMode;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_debug);
-
- if (!Settings.SHOULD_SHOW_LXX_SUGGESTION_UI) {
- removePreference(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI);
- }
-
- final PreferenceGroup dictDumpPreferenceGroup =
- (PreferenceGroup)findPreference(PREF_KEY_DUMP_DICTS);
- for (final String dictName : DictionaryFacilitatorImpl.DICT_TYPE_TO_CLASS.keySet()) {
- final Preference pref = new DictDumpPreference(getActivity(), dictName);
- pref.setOnPreferenceClickListener(this);
- dictDumpPreferenceGroup.addPreference(pref);
- }
- final Resources res = getResources();
- setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
- res.getInteger(R.integer.config_key_preview_show_up_duration));
- setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
- res.getInteger(R.integer.config_key_preview_dismiss_duration));
- final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction(
- res, R.fraction.config_key_preview_show_up_start_scale);
- final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction(
- res, R.fraction.config_key_preview_dismiss_end_scale);
- setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
- defaultKeyPreviewShowUpStartScale);
- setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
- defaultKeyPreviewShowUpStartScale);
- setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
- defaultKeyPreviewDismissEndScale);
- setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
- defaultKeyPreviewDismissEndScale);
- setupKeyboardHeight(
- DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE);
-
- mServiceNeedsRestart = false;
- mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE);
- updateDebugMode();
- }
-
- private static class DictDumpPreference extends Preference {
- public final String mDictName;
-
- public DictDumpPreference(final Context context, final String dictName) {
- super(context);
- setKey(PREF_KEY_DUMP_DICT_PREFIX + dictName);
- setTitle("Dump " + dictName + " dictionary");
- mDictName = dictName;
- }
- }
-
- @Override
- public boolean onPreferenceClick(final Preference pref) {
- final Context context = getActivity();
- if (pref instanceof DictDumpPreference) {
- final DictDumpPreference dictDumpPref = (DictDumpPreference)pref;
- final String dictName = dictDumpPref.mDictName;
- final Intent intent = new Intent(
- DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
- intent.putExtra(DictionaryDumpBroadcastReceiver.DICTIONARY_NAME_KEY, dictName);
- context.sendBroadcast(intent);
- return true;
- }
- return true;
- }
-
- @Override
- public void onStop() {
- super.onStop();
- if (mServiceNeedsRestart) {
- Process.killProcess(Process.myPid());
- }
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- if (key.equals(DebugSettings.PREF_DEBUG_MODE) && mDebugMode != null) {
- mDebugMode.setChecked(prefs.getBoolean(DebugSettings.PREF_DEBUG_MODE, false));
- updateDebugMode();
- mServiceNeedsRestart = true;
- return;
- }
- if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH)) {
- mServiceNeedsRestart = true;
- return;
- }
- }
-
- private void updateDebugMode() {
- boolean isDebugMode = mDebugMode.isChecked();
- final String version = getString(
- R.string.version_text, ApplicationUtils.getVersionName(getActivity()));
- if (!isDebugMode) {
- mDebugMode.setTitle(version);
- mDebugMode.setSummary(null);
- } else {
- mDebugMode.setTitle(getString(R.string.prefs_debug_mode));
- mDebugMode.setSummary(version);
- }
- }
-
- private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) {
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey);
- if (pref == null) {
- return;
- }
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- private static final float PERCENTAGE_FLOAT = 100.0f;
-
- private float getValueFromPercentage(final int percentage) {
- return percentage / PERCENTAGE_FLOAT;
- }
-
- private int getPercentageFromValue(final float floatValue) {
- return (int)(floatValue * PERCENTAGE_FLOAT);
- }
-
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putFloat(key, getValueFromPercentage(value)).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return getPercentageFromValue(
- Settings.readKeyPreviewAnimationScale(prefs, key, defaultValue));
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return getPercentageFromValue(defaultValue);
- }
-
- @Override
- public String getValueText(final int value) {
- if (value < 0) {
- return res.getString(R.string.settings_system_default);
- }
- return String.format(Locale.ROOT, "%d%%", value);
- }
-
- @Override
- public void feedbackValue(final int value) {}
- });
- }
-
- private void setupKeyPreviewAnimationDuration(final String prefKey, final int defaultValue) {
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey);
- if (pref == null) {
- return;
- }
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putInt(key, value).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return Settings.readKeyPreviewAnimationDuration(prefs, key, defaultValue);
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return defaultValue;
- }
-
- @Override
- public String getValueText(final int value) {
- return res.getString(R.string.abbreviation_unit_milliseconds, value);
- }
-
- @Override
- public void feedbackValue(final int value) {}
- });
- }
-
- private void setupKeyboardHeight(final String prefKey, final float defaultValue) {
- final SharedPreferences prefs = getSharedPreferences();
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey);
- if (pref == null) {
- return;
- }
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- private static final float PERCENTAGE_FLOAT = 100.0f;
- private float getValueFromPercentage(final int percentage) {
- return percentage / PERCENTAGE_FLOAT;
- }
-
- private int getPercentageFromValue(final float floatValue) {
- return (int)(floatValue * PERCENTAGE_FLOAT);
- }
-
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putFloat(key, getValueFromPercentage(value)).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return getPercentageFromValue(Settings.readKeyboardHeight(prefs, defaultValue));
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return getPercentageFromValue(defaultValue);
- }
-
- @Override
- public String getValueText(final int value) {
- return String.format(Locale.ROOT, "%d%%", value);
- }
-
- @Override
- public void feedbackValue(final int value) {}
- });
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java
deleted file mode 100644
index 22b0655b4..000000000
--- a/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.os.Bundle;
-
-import com.android.inputmethod.latin.R;
-
-/**
- * "Gesture typing preferences" settings sub screen.
- *
- * This settings sub screen handles the following gesture typing preferences.
- * - Enable gesture typing
- * - Dynamic floating preview
- * - Show gesture trail
- * - Phrase gesture
- */
-public final class GestureSettingsFragment extends SubScreenFragment {
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_gesture);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
deleted file mode 100644
index 5c416ab18..000000000
--- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-/**
- * Collection of device specific preference constants.
- */
-public class LocalSettingsConstants {
- // Preference file for storing preferences that are tied to a device
- // and are not backed up.
- public static final String PREFS_FILE = "local_prefs";
-
- // Preference key for the current account.
- // Do not restore.
- public static final String PREF_ACCOUNT_NAME = "pref_account_name";
- // Preference key for enabling cloud sync feature.
- // Do not restore.
- public static final String PREF_ENABLE_CLOUD_SYNC = "pref_enable_cloud_sync";
-
- // List of preference keys to skip from being restored by backup agent.
- // These preferences are tied to a device and hence should not be restored.
- // e.g. account name.
- // Ideally they could have been kept in a separate file that wasn't backed up
- // however the preference UI currently only deals with the default
- // shared preferences which makes it non-trivial to move these out to
- // a different shared preferences file.
- public static final String[] PREFS_TO_SKIP_RESTORING = new String[] {
- PREF_ACCOUNT_NAME,
- PREF_ENABLE_CLOUD_SYNC,
- // The debug settings are not restored on a new device.
- // If a feature relies on these, it should ensure that the defaults are
- // correctly set for it to work on a new device.
- DebugSettings.PREF_DEBUG_MODE,
- DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH,
- DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS,
- DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE,
- DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
- DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
- DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
- DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
- DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
- DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
- DebugSettings.PREF_RESIZE_KEYBOARD,
- DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI,
- DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW
- };
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
deleted file mode 100644
index d9858e61f..000000000
--- a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.Preference;
-
-import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodManager;
-
-/**
- * "Preferences" settings sub screen.
- *
- * This settings sub screen handles the following input preferences.
- * - Auto-capitalization
- * - Double-space period
- * - Vibrate on keypress
- * - Sound on keypress
- * - Popup on keypress
- * - Voice input key
- */
-public final class PreferencesSettingsFragment extends SubScreenFragment {
-
- private static final boolean VOICE_IME_ENABLED =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
-
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_preferences);
-
- final Resources res = getResources();
- final Context context = getActivity();
-
- // When we are called from the Settings application but we are not already running, some
- // singleton and utility classes may not have been initialized. We have to call
- // initialization method of these classes here. See {@link LatinIME#onCreate()}.
- RichInputMethodManager.init(context);
-
- final boolean showVoiceKeyOption = res.getBoolean(
- R.bool.config_enable_show_voice_key_option);
- if (!showVoiceKeyOption) {
- removePreference(Settings.PREF_VOICE_INPUT_KEY);
- }
- if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) {
- removePreference(Settings.PREF_VIBRATE_ON);
- }
- if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
- removePreference(Settings.PREF_POPUP_ON);
- }
-
- refreshEnablingsOfKeypressSoundAndVibrationSettings();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final Preference voiceInputKeyOption = findPreference(Settings.PREF_VOICE_INPUT_KEY);
- if (voiceInputKeyOption != null) {
- RichInputMethodManager.getInstance().refreshSubtypeCaches();
- voiceInputKeyOption.setEnabled(VOICE_IME_ENABLED);
- voiceInputKeyOption.setSummary(VOICE_IME_ENABLED
- ? null : getText(R.string.voice_input_disabled_summary));
- }
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- final Resources res = getResources();
- if (key.equals(Settings.PREF_POPUP_ON)) {
- setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
- Settings.readKeyPreviewPopupEnabled(prefs, res));
- }
- refreshEnablingsOfKeypressSoundAndVibrationSettings();
- }
-
- private void refreshEnablingsOfKeypressSoundAndVibrationSettings() {
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS,
- Settings.readVibrationEnabled(prefs, res));
- setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME,
- Settings.readKeypressSoundEnabled(prefs, res));
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java b/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java
deleted file mode 100644
index 91444604d..000000000
--- a/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.RadioButton;
-
-import com.android.inputmethod.latin.R;
-
-/**
- * Radio Button preference
- */
-public class RadioButtonPreference extends Preference {
- interface OnRadioButtonClickedListener {
- /**
- * Called when this preference needs to be saved its state.
- *
- * @param preference This preference.
- */
- public void onRadioButtonClicked(RadioButtonPreference preference);
- }
-
- private boolean mIsSelected;
- private RadioButton mRadioButton;
- private OnRadioButtonClickedListener mListener;
- private final View.OnClickListener mClickListener = new View.OnClickListener() {
- @Override
- public void onClick(final View v) {
- callListenerOnRadioButtonClicked();
- }
- };
-
- public RadioButtonPreference(final Context context) {
- this(context, null);
- }
-
- public RadioButtonPreference(final Context context, final AttributeSet attrs) {
- this(context, attrs, android.R.attr.preferenceStyle);
- }
-
- public RadioButtonPreference(final Context context, final AttributeSet attrs,
- final int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setWidgetLayoutResource(R.layout.radio_button_preference_widget);
- }
-
- public void setOnRadioButtonClickedListener(final OnRadioButtonClickedListener listener) {
- mListener = listener;
- }
-
- void callListenerOnRadioButtonClicked() {
- if (mListener != null) {
- mListener.onRadioButtonClicked(this);
- }
- }
-
- @Override
- protected void onBindView(final View view) {
- super.onBindView(view);
- mRadioButton = (RadioButton)view.findViewById(R.id.radio_button);
- mRadioButton.setChecked(mIsSelected);
- mRadioButton.setOnClickListener(mClickListener);
- view.setOnClickListener(mClickListener);
- }
-
- public boolean isSelected() {
- return mIsSelected;
- }
-
- public void setSelected(final boolean selected) {
- if (selected == mIsSelected) {
- return;
- }
- mIsSelected = selected;
- if (mRadioButton != null) {
- mRadioButton.setChecked(selected);
- }
- notifyChanged();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/settings/SeekBarDialogPreference.java
deleted file mode 100644
index 802574aa8..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SeekBarDialogPreference.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.TypedArray;
-import android.preference.DialogPreference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.android.inputmethod.latin.R;
-
-public final class SeekBarDialogPreference extends DialogPreference
- implements SeekBar.OnSeekBarChangeListener {
- public interface ValueProxy {
- public int readValue(final String key);
- public int readDefaultValue(final String key);
- public void writeValue(final int value, final String key);
- public void writeDefaultValue(final String key);
- public String getValueText(final int value);
- public void feedbackValue(final int value);
- }
-
- private final int mMaxValue;
- private final int mMinValue;
- private final int mStepValue;
-
- private TextView mValueView;
- private SeekBar mSeekBar;
-
- private ValueProxy mValueProxy;
-
- public SeekBarDialogPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.SeekBarDialogPreference, 0, 0);
- mMaxValue = a.getInt(R.styleable.SeekBarDialogPreference_maxValue, 0);
- mMinValue = a.getInt(R.styleable.SeekBarDialogPreference_minValue, 0);
- mStepValue = a.getInt(R.styleable.SeekBarDialogPreference_stepValue, 0);
- a.recycle();
- setDialogLayoutResource(R.layout.seek_bar_dialog);
- }
-
- public void setInterface(final ValueProxy proxy) {
- mValueProxy = proxy;
- final int value = mValueProxy.readValue(getKey());
- setSummary(mValueProxy.getValueText(value));
- }
-
- @Override
- protected View onCreateDialogView() {
- final View view = super.onCreateDialogView();
- mSeekBar = (SeekBar)view.findViewById(R.id.seek_bar_dialog_bar);
- mSeekBar.setMax(mMaxValue - mMinValue);
- mSeekBar.setOnSeekBarChangeListener(this);
- mValueView = (TextView)view.findViewById(R.id.seek_bar_dialog_value);
- return view;
- }
-
- private int getProgressFromValue(final int value) {
- return value - mMinValue;
- }
-
- private int getValueFromProgress(final int progress) {
- return progress + mMinValue;
- }
-
- private int clipValue(final int value) {
- final int clippedValue = Math.min(mMaxValue, Math.max(mMinValue, value));
- if (mStepValue <= 1) {
- return clippedValue;
- }
- return clippedValue - (clippedValue % mStepValue);
- }
-
- private int getClippedValueFromProgress(final int progress) {
- return clipValue(getValueFromProgress(progress));
- }
-
- @Override
- protected void onBindDialogView(final View view) {
- final int value = mValueProxy.readValue(getKey());
- mValueView.setText(mValueProxy.getValueText(value));
- mSeekBar.setProgress(getProgressFromValue(clipValue(value)));
- }
-
- @Override
- protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
- builder.setPositiveButton(android.R.string.ok, this)
- .setNegativeButton(android.R.string.cancel, this)
- .setNeutralButton(R.string.button_default, this);
- }
-
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- super.onClick(dialog, which);
- final String key = getKey();
- if (which == DialogInterface.BUTTON_NEUTRAL) {
- final int value = mValueProxy.readDefaultValue(key);
- setSummary(mValueProxy.getValueText(value));
- mValueProxy.writeDefaultValue(key);
- return;
- }
- if (which == DialogInterface.BUTTON_POSITIVE) {
- final int value = getClippedValueFromProgress(mSeekBar.getProgress());
- setSummary(mValueProxy.getValueText(value));
- mValueProxy.writeValue(value, key);
- return;
- }
- }
-
- @Override
- public void onProgressChanged(final SeekBar seekBar, final int progress,
- final boolean fromUser) {
- final int value = getClippedValueFromProgress(progress);
- mValueView.setText(mValueProxy.getValueText(value));
- if (!fromUser) {
- mSeekBar.setProgress(getProgressFromValue(value));
- }
- }
-
- @Override
- public void onStartTrackingTouch(final SeekBar seekBar) {}
-
- @Override
- public void onStopTrackingTouch(final SeekBar seekBar) {
- mValueProxy.feedbackValue(getClippedValueFromProgress(seekBar.getProgress()));
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
deleted file mode 100644
index 940f1bdfc..000000000
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Build;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-import com.android.inputmethod.compat.BuildCompatUtils;
-import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
-import com.android.inputmethod.latin.InputAttributes;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.RunInLocale;
-import com.android.inputmethod.latin.utils.StatsUtils;
-
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.annotation.Nonnull;
-
-public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = Settings.class.getSimpleName();
- // Settings screens
- public static final String SCREEN_ACCOUNTS = "screen_accounts";
- public static final String SCREEN_THEME = "screen_theme";
- public static final String SCREEN_DEBUG = "screen_debug";
- // In the same order as xml/prefs.xml
- public static final String PREF_AUTO_CAP = "auto_cap";
- public static final String PREF_VIBRATE_ON = "vibrate_on";
- public static final String PREF_SOUND_ON = "sound_on";
- public static final String PREF_POPUP_ON = "popup_on";
- // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead.
- public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode";
- public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key";
- public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
- public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
- // PREF_AUTO_CORRECTION_THRESHOLD_OBSOLETE is obsolete. Use PREF_AUTO_CORRECTION instead.
- public static final String PREF_AUTO_CORRECTION_THRESHOLD_OBSOLETE =
- "auto_correction_threshold";
- public static final String PREF_AUTO_CORRECTION = "pref_key_auto_correction";
- // PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE is obsolete. Use PREF_SHOW_SUGGESTIONS instead.
- public static final String PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE = "show_suggestions_setting";
- public static final String PREF_SHOW_SUGGESTIONS = "show_suggestions";
- public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
- public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts";
- public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
- "pref_key_use_double_space_period";
- public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
- "pref_key_block_potentially_offensive";
- public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS =
- BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT;
- public static final boolean SHOULD_SHOW_LXX_SUGGESTION_UI =
- BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
- public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
- "pref_show_language_switch_key";
- public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
- "pref_include_other_imes_in_language_switch_list";
- public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
- public static final String PREF_ENABLE_SPLIT_KEYBOARD = "pref_split_keyboard";
- // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
- public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
- "pref_key_preview_popup_dismiss_delay";
- public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
- public static final String PREF_GESTURE_INPUT = "gesture_input";
- public static final String PREF_VIBRATION_DURATION_SETTINGS =
- "pref_vibration_duration_settings";
- public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume";
- public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
- public static final String PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY =
- "pref_enable_emoji_alt_physical_key";
- public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
- public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
- "pref_gesture_floating_preview_text";
- public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon";
-
- public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
-
- public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
- // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
- // This is being used only for the backward compatibility.
- private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
- "pref_suppress_language_switch_key";
-
- private static final String PREF_LAST_USED_PERSONALIZATION_TOKEN =
- "pref_last_used_personalization_token";
- private static final String PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME =
- "pref_last_used_personalization_dict_wiped_time";
- private static final String PREF_CORPUS_HANDLES_FOR_PERSONALIZATION =
- "pref_corpus_handles_for_personalization";
-
- // Emoji
- public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys";
- public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id";
- public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
-
- private static final float UNDEFINED_PREFERENCE_VALUE_FLOAT = -1.0f;
- private static final int UNDEFINED_PREFERENCE_VALUE_INT = -1;
-
- private Context mContext;
- private Resources mRes;
- private SharedPreferences mPrefs;
- private SettingsValues mSettingsValues;
- private final ReentrantLock mSettingsValuesLock = new ReentrantLock();
-
- private static final Settings sInstance = new Settings();
-
- public static Settings getInstance() {
- return sInstance;
- }
-
- public static void init(final Context context) {
- sInstance.onCreate(context);
- }
-
- private Settings() {
- // Intentional empty constructor for singleton.
- }
-
- private void onCreate(final Context context) {
- mContext = context;
- mRes = context.getResources();
- mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
- mPrefs.registerOnSharedPreferenceChangeListener(this);
- upgradeAutocorrectionSettings(mPrefs, mRes);
- }
-
- public void onDestroy() {
- mPrefs.unregisterOnSharedPreferenceChangeListener(this);
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- mSettingsValuesLock.lock();
- try {
- if (mSettingsValues == null) {
- // TODO: Introduce a static function to register this class and ensure that
- // loadSettings must be called before "onSharedPreferenceChanged" is called.
- Log.w(TAG, "onSharedPreferenceChanged called before loadSettings.");
- return;
- }
- loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
- StatsUtils.onLoadSettings(mSettingsValues);
- } finally {
- mSettingsValuesLock.unlock();
- }
- }
-
- public void loadSettings(final Context context, final Locale locale,
- @Nonnull final InputAttributes inputAttributes) {
- mSettingsValuesLock.lock();
- mContext = context;
- try {
- final SharedPreferences prefs = mPrefs;
- final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
- @Override
- protected SettingsValues job(final Resources res) {
- return new SettingsValues(context, prefs, res, inputAttributes);
- }
- };
- mSettingsValues = job.runInLocale(mRes, locale);
- } finally {
- mSettingsValuesLock.unlock();
- }
- }
-
- // TODO: Remove this method and add proxy method to SettingsValues.
- public SettingsValues getCurrent() {
- return mSettingsValues;
- }
-
- public boolean isInternal() {
- return mSettingsValues.mIsInternal;
- }
-
- public static int readScreenMetrics(final Resources res) {
- return res.getInteger(R.integer.config_screen_metrics);
- }
-
- // Accessed from the settings interface, hence public
- public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
- final Resources res) {
- return prefs.getBoolean(PREF_SOUND_ON,
- res.getBoolean(R.bool.config_default_sound_enabled));
- }
-
- public static boolean readVibrationEnabled(final SharedPreferences prefs,
- final Resources res) {
- final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator();
- return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON,
- res.getBoolean(R.bool.config_default_vibration_enabled));
- }
-
- public static boolean readAutoCorrectEnabled(final SharedPreferences prefs,
- final Resources res) {
- return prefs.getBoolean(PREF_AUTO_CORRECTION, true);
- }
-
- public static float readPlausibilityThreshold(final Resources res) {
- return Float.parseFloat(res.getString(R.string.plausibility_threshold));
- }
-
- public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs,
- final Resources res) {
- return prefs.getBoolean(PREF_BLOCK_POTENTIALLY_OFFENSIVE,
- res.getBoolean(R.bool.config_block_potentially_offensive));
- }
-
- public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) {
- return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config);
- }
-
- public static boolean readGestureInputEnabled(final SharedPreferences prefs,
- final Resources res) {
- return readFromBuildConfigIfGestureInputEnabled(res)
- && prefs.getBoolean(PREF_GESTURE_INPUT, true);
- }
-
- public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) {
- return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option);
- }
-
- public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs,
- final Resources res) {
- final boolean defaultKeyPreviewPopup = res.getBoolean(
- R.bool.config_default_key_preview_popup);
- if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
- return defaultKeyPreviewPopup;
- }
- return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup);
- }
-
- public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs,
- final Resources res) {
- return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
- Integer.toString(res.getInteger(
- R.integer.config_key_preview_linger_timeout))));
- }
-
- public static boolean readShowsLanguageSwitchKey(final SharedPreferences prefs) {
- if (prefs.contains(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
- final boolean suppressLanguageSwitchKey = prefs.getBoolean(
- PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
- final SharedPreferences.Editor editor = prefs.edit();
- editor.remove(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY);
- editor.putBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, !suppressLanguageSwitchKey);
- editor.apply();
- }
- return prefs.getBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, true);
- }
-
- public static String readPrefAdditionalSubtypes(final SharedPreferences prefs,
- final Resources res) {
- final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(
- res.getStringArray(R.array.predefined_subtypes));
- return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes);
- }
-
- public static void writePrefAdditionalSubtypes(final SharedPreferences prefs,
- final String prefSubtypes) {
- prefs.edit().putString(PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply();
- }
-
- public static float readKeypressSoundVolume(final SharedPreferences prefs,
- final Resources res) {
- final float volume = prefs.getFloat(
- PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT);
- return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume
- : readDefaultKeypressSoundVolume(res);
- }
-
- // Default keypress sound volume for unknown devices.
- // The negative value means system default.
- private static final String DEFAULT_KEYPRESS_SOUND_VOLUME = Float.toString(-1.0f);
-
- public static float readDefaultKeypressSoundVolume(final Resources res) {
- return Float.parseFloat(ResourceUtils.getDeviceOverrideValue(res,
- R.array.keypress_volumes, DEFAULT_KEYPRESS_SOUND_VOLUME));
- }
-
- public static int readKeyLongpressTimeout(final SharedPreferences prefs,
- final Resources res) {
- final int milliseconds = prefs.getInt(
- PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
- return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
- : readDefaultKeyLongpressTimeout(res);
- }
-
- public static int readDefaultKeyLongpressTimeout(final Resources res) {
- return res.getInteger(R.integer.config_default_longpress_key_timeout);
- }
-
- public static int readKeypressVibrationDuration(final SharedPreferences prefs,
- final Resources res) {
- final int milliseconds = prefs.getInt(
- PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT);
- return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
- : readDefaultKeypressVibrationDuration(res);
- }
-
- // Default keypress vibration duration for unknown devices.
- // The negative value means system default.
- private static final String DEFAULT_KEYPRESS_VIBRATION_DURATION = Integer.toString(-1);
-
- public static int readDefaultKeypressVibrationDuration(final Resources res) {
- return Integer.parseInt(ResourceUtils.getDeviceOverrideValue(res,
- R.array.keypress_vibration_durations, DEFAULT_KEYPRESS_VIBRATION_DURATION));
- }
-
- public static float readKeyPreviewAnimationScale(final SharedPreferences prefs,
- final String prefKey, final float defaultValue) {
- final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT);
- return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue;
- }
-
- public static int readKeyPreviewAnimationDuration(final SharedPreferences prefs,
- final String prefKey, final int defaultValue) {
- final int milliseconds = prefs.getInt(prefKey, UNDEFINED_PREFERENCE_VALUE_INT);
- return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue;
- }
-
- public static float readKeyboardHeight(final SharedPreferences prefs,
- final float defaultValue) {
- final float percentage = prefs.getFloat(
- DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, UNDEFINED_PREFERENCE_VALUE_FLOAT);
- return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue;
- }
-
- public static boolean readUseFullscreenMode(final Resources res) {
- return res.getBoolean(R.bool.config_use_fullscreen_mode);
- }
-
- public static boolean readShowSetupWizardIcon(final SharedPreferences prefs,
- final Context context) {
- if (!prefs.contains(PREF_SHOW_SETUP_WIZARD_ICON)) {
- final ApplicationInfo appInfo = context.getApplicationInfo();
- final boolean isApplicationInSystemImage =
- (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // Default value
- return !isApplicationInSystemImage;
- }
- return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, false);
- }
-
- public static boolean readHasHardwareKeyboard(final Configuration conf) {
- // The standard way of finding out whether we have a hardware keyboard. This code is taken
- // from InputMethodService#onEvaluateInputShown, which canonically determines this.
- // In a nutshell, we have a keyboard if the configuration says the type of hardware keyboard
- // is NOKEYS and if it's not hidden (e.g. folded inside the device).
- return conf.keyboard != Configuration.KEYBOARD_NOKEYS
- && conf.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_YES;
- }
-
- public static boolean isInternal(final SharedPreferences prefs) {
- return prefs.getBoolean(PREF_KEY_IS_INTERNAL, false);
- }
-
- public void writeLastUsedPersonalizationToken(byte[] token) {
- if (token == null) {
- mPrefs.edit().remove(PREF_LAST_USED_PERSONALIZATION_TOKEN).apply();
- } else {
- final String tokenStr = StringUtils.byteArrayToHexString(token);
- mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply();
- }
- }
-
- public byte[] readLastUsedPersonalizationToken() {
- final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null);
- return StringUtils.hexStringToByteArray(tokenStr);
- }
-
- public void writeLastPersonalizationDictWipedTime(final long timestamp) {
- mPrefs.edit().putLong(PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME, timestamp).apply();
- }
-
- public long readLastPersonalizationDictGeneratedTime() {
- return mPrefs.getLong(PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME, 0);
- }
-
- public void writeCorpusHandlesForPersonalization(final Set<String> corpusHandles) {
- mPrefs.edit().putStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, corpusHandles).apply();
- }
-
- public Set<String> readCorpusHandlesForPersonalization() {
- final Set<String> emptySet = Collections.emptySet();
- return mPrefs.getStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, emptySet);
- }
-
- public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) {
- prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply();
- }
-
- public static String readEmojiRecentKeys(final SharedPreferences prefs) {
- return prefs.getString(PREF_EMOJI_RECENT_KEYS, "");
- }
-
- public static void writeLastTypedEmojiCategoryPageId(
- final SharedPreferences prefs, final int categoryId, final int categoryPageId) {
- final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
- prefs.edit().putInt(key, categoryPageId).apply();
- }
-
- public static int readLastTypedEmojiCategoryPageId(
- final SharedPreferences prefs, final int categoryId) {
- final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
- return prefs.getInt(key, 0);
- }
-
- public static void writeLastShownEmojiCategoryId(
- final SharedPreferences prefs, final int categoryId) {
- prefs.edit().putInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, categoryId).apply();
- }
-
- public static int readLastShownEmojiCategoryId(
- final SharedPreferences prefs, final int defValue) {
- return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, defValue);
- }
-
- private void upgradeAutocorrectionSettings(final SharedPreferences prefs, final Resources res) {
- final String thresholdSetting =
- prefs.getString(PREF_AUTO_CORRECTION_THRESHOLD_OBSOLETE, null);
- if (thresholdSetting != null) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.remove(PREF_AUTO_CORRECTION_THRESHOLD_OBSOLETE);
- final String autoCorrectionOff =
- res.getString(R.string.auto_correction_threshold_mode_index_off);
- if (thresholdSetting.equals(autoCorrectionOff)) {
- editor.putBoolean(PREF_AUTO_CORRECTION, false);
- } else {
- editor.putBoolean(PREF_AUTO_CORRECTION, true);
- }
- editor.commit();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
deleted file mode 100644
index ac1657762..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import com.android.inputmethod.latin.permissions.PermissionsManager;
-import com.android.inputmethod.latin.utils.FragmentUtils;
-import com.android.inputmethod.latin.utils.StatsUtils;
-import com.android.inputmethod.latin.utils.StatsUtilsManager;
-
-import android.app.ActionBar;
-import android.content.Intent;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import androidx.core.app.ActivityCompat;
-import android.view.MenuItem;
-
-public final class SettingsActivity extends PreferenceActivity
- implements ActivityCompat.OnRequestPermissionsResultCallback {
- private static final String DEFAULT_FRAGMENT = SettingsFragment.class.getName();
-
- public static final String EXTRA_SHOW_HOME_AS_UP = "show_home_as_up";
- public static final String EXTRA_ENTRY_KEY = "entry";
- public static final String EXTRA_ENTRY_VALUE_LONG_PRESS_COMMA = "long_press_comma";
- public static final String EXTRA_ENTRY_VALUE_APP_ICON = "app_icon";
- public static final String EXTRA_ENTRY_VALUE_NOTICE_DIALOG = "important_notice";
- public static final String EXTRA_ENTRY_VALUE_SYSTEM_SETTINGS = "system_settings";
-
- private boolean mShowHomeAsUp;
-
- @Override
- protected void onCreate(final Bundle savedState) {
- super.onCreate(savedState);
- final ActionBar actionBar = getActionBar();
- final Intent intent = getIntent();
- if (actionBar != null) {
- mShowHomeAsUp = intent.getBooleanExtra(EXTRA_SHOW_HOME_AS_UP, true);
- actionBar.setDisplayHomeAsUpEnabled(mShowHomeAsUp);
- actionBar.setHomeButtonEnabled(mShowHomeAsUp);
- }
- StatsUtils.onSettingsActivity(
- intent.hasExtra(EXTRA_ENTRY_KEY) ? intent.getStringExtra(EXTRA_ENTRY_KEY)
- : EXTRA_ENTRY_VALUE_SYSTEM_SETTINGS);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (mShowHomeAsUp && item.getItemId() == android.R.id.home) {
- finish();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public Intent getIntent() {
- final Intent intent = super.getIntent();
- final String fragment = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
- if (fragment == null) {
- intent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT);
- }
- intent.putExtra(EXTRA_NO_HEADERS, true);
- return intent;
- }
-
- @Override
- public boolean isValidFragment(final String fragmentName) {
- return FragmentUtils.isValidFragment(fragmentName);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- PermissionsManager.get(this).onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
deleted file mode 100644
index 874f221c6..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
-import android.provider.Settings.Secure;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.utils.ApplicationUtils;
-import com.android.inputmethod.latin.utils.FeedbackUtils;
-import com.android.inputmethodcommon.InputMethodSettingsFragment;
-
-public final class SettingsFragment extends InputMethodSettingsFragment {
- // We don't care about menu grouping.
- private static final int NO_MENU_GROUP = Menu.NONE;
- // The first menu item id and order.
- private static final int MENU_ABOUT = Menu.FIRST;
- // The second menu item id and order.
- private static final int MENU_HELP_AND_FEEDBACK = Menu.FIRST + 1;
-
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- setHasOptionsMenu(true);
- setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
- setSubtypeEnablerTitle(R.string.select_language);
- addPreferencesFromResource(R.xml.prefs);
- final PreferenceScreen preferenceScreen = getPreferenceScreen();
- preferenceScreen.setTitle(
- ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class));
- if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
- final Preference accountsPreference = findPreference(Settings.SCREEN_ACCOUNTS);
- preferenceScreen.removePreference(accountsPreference);
- }
- }
-
- @Override
- public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
- if (FeedbackUtils.isHelpAndFeedbackFormSupported()) {
- menu.add(NO_MENU_GROUP, MENU_HELP_AND_FEEDBACK /* itemId */,
- MENU_HELP_AND_FEEDBACK /* order */, R.string.help_and_feedback);
- }
- final int aboutResId = FeedbackUtils.getAboutKeyboardTitleResId();
- if (aboutResId != 0) {
- menu.add(NO_MENU_GROUP, MENU_ABOUT /* itemId */, MENU_ABOUT /* order */, aboutResId);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- final Activity activity = getActivity();
- if (!isUserSetupComplete(activity)) {
- // If setup is not complete, it's not safe to launch Help or other activities
- // because they might go to the Play Store. See b/19866981.
- return true;
- }
- final int itemId = item.getItemId();
- if (itemId == MENU_HELP_AND_FEEDBACK) {
- FeedbackUtils.showHelpAndFeedbackForm(activity);
- return true;
- }
- if (itemId == MENU_ABOUT) {
- final Intent aboutIntent = FeedbackUtils.getAboutKeyboardIntent(activity);
- if (aboutIntent != null) {
- startActivity(aboutIntent);
- return true;
- }
- }
- return super.onOptionsItemSelected(item);
- }
-
- private static boolean isUserSetupComplete(final Activity activity) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- return true;
- }
- return Secure.getInt(activity.getContentResolver(), "user_setup_complete", 0) != 0;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
deleted file mode 100644
index 57018244f..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Build;
-import android.util.Log;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.compat.AppWorkaroundsUtils;
-import com.android.inputmethod.latin.InputAttributes;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.utils.AsyncResultHolder;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
-
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * When you call the constructor of this class, you may want to change the current system locale by
- * using {@link com.android.inputmethod.latin.utils.RunInLocale}.
- */
-// Non-final for testing via mock library.
-public class SettingsValues {
- private static final String TAG = SettingsValues.class.getSimpleName();
- // "floatMaxValue" and "floatNegativeInfinity" are special marker strings for
- // Float.NEGATIVE_INFINITE and Float.MAX_VALUE. Currently used for auto-correction settings.
- private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue";
- private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
- private static final int TIMEOUT_TO_GET_TARGET_PACKAGE = 5; // seconds
- public static final float DEFAULT_SIZE_SCALE = 1.0f; // 100%
-
- // From resources:
- public final SpacingAndPunctuations mSpacingAndPunctuations;
- public final int mDelayInMillisecondsToUpdateOldSuggestions;
- public final long mDoubleSpacePeriodTimeout;
- // From configuration:
- public final Locale mLocale;
- public final boolean mHasHardwareKeyboard;
- public final int mDisplayOrientation;
- // From preferences, in the same order as xml/prefs.xml:
- public final boolean mAutoCap;
- public final boolean mVibrateOn;
- public final boolean mSoundOn;
- public final boolean mKeyPreviewPopupOn;
- public final boolean mShowsVoiceInputKey;
- public final boolean mIncludesOtherImesInLanguageSwitchList;
- public final boolean mShowsLanguageSwitchKey;
- public final boolean mUseContactsDict;
- public final boolean mUsePersonalizedDicts;
- public final boolean mUseDoubleSpacePeriod;
- public final boolean mBlockPotentiallyOffensive;
- // Use bigrams to predict the next word when there is no input for it yet
- public final boolean mBigramPredictionEnabled;
- public final boolean mGestureInputEnabled;
- public final boolean mGestureTrailEnabled;
- public final boolean mGestureFloatingPreviewTextEnabled;
- public final boolean mSlidingKeyInputPreviewEnabled;
- public final int mKeyLongpressTimeout;
- public final boolean mEnableEmojiAltPhysicalKey;
- public final boolean mShowAppIcon;
- public final boolean mIsShowAppIconSettingInPreferences;
- public final boolean mCloudSyncEnabled;
- public final boolean mEnableMetricsLogging;
- public final boolean mShouldShowLxxSuggestionUi;
- // Use split layout for keyboard.
- public final boolean mIsSplitKeyboardEnabled;
- public final int mScreenMetrics;
-
- // From the input box
- @Nonnull
- public final InputAttributes mInputAttributes;
-
- // Deduced settings
- public final int mKeypressVibrationDuration;
- public final float mKeypressSoundVolume;
- public final int mKeyPreviewPopupDismissDelay;
- private final boolean mAutoCorrectEnabled;
- public final float mAutoCorrectionThreshold;
- public final float mPlausibilityThreshold;
- public final boolean mAutoCorrectionEnabledPerUserSettings;
- private final boolean mSuggestionsEnabledPerUserSettings;
- private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
-
- // Debug settings
- public final boolean mIsInternal;
- public final boolean mHasCustomKeyPreviewAnimationParams;
- public final boolean mHasKeyboardResize;
- public final float mKeyboardHeightScale;
- public final int mKeyPreviewShowUpDuration;
- public final int mKeyPreviewDismissDuration;
- public final float mKeyPreviewShowUpStartXScale;
- public final float mKeyPreviewShowUpStartYScale;
- public final float mKeyPreviewDismissEndXScale;
- public final float mKeyPreviewDismissEndYScale;
-
- @Nullable public final String mAccount;
-
- public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res,
- @Nonnull final InputAttributes inputAttributes) {
- mLocale = res.getConfiguration().locale;
- // Get the resources
- mDelayInMillisecondsToUpdateOldSuggestions =
- res.getInteger(R.integer.config_delay_in_milliseconds_to_update_old_suggestions);
- mSpacingAndPunctuations = new SpacingAndPunctuations(res);
-
- // Store the input attributes
- mInputAttributes = inputAttributes;
-
- // Get the settings preferences
- mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
- mVibrateOn = Settings.readVibrationEnabled(prefs, res);
- mSoundOn = Settings.readKeypressSoundEnabled(prefs, res);
- mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res);
- mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
- DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
- mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res)
- && mInputAttributes.mShouldShowVoiceInputKey
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
- mIncludesOtherImesInLanguageSwitchList = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS
- ? prefs.getBoolean(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false)
- : true /* forcibly */;
- mShowsLanguageSwitchKey = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS
- ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */;
- mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
- mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
- mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true)
- && inputAttributes.mIsGeneralTextInput;
- mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res);
- mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(prefs, res);
- final String autoCorrectionThresholdRawValue = mAutoCorrectEnabled
- ? res.getString(R.string.auto_correction_threshold_mode_index_modest)
- : res.getString(R.string.auto_correction_threshold_mode_index_off);
- mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
- mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
- mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
- mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
- mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false);
- mScreenMetrics = Settings.readScreenMetrics(res);
-
- mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI
- && prefs.getBoolean(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, true);
- // Compute other readable settings
- mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
- mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
- mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res);
- mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
- mEnableEmojiAltPhysicalKey = prefs.getBoolean(
- Settings.PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY, true);
- mShowAppIcon = Settings.readShowSetupWizardIcon(prefs, context);
- mIsShowAppIconSettingInPreferences = prefs.contains(Settings.PREF_SHOW_SETUP_WIZARD_ICON);
- mAutoCorrectionThreshold = readAutoCorrectionThreshold(res,
- autoCorrectionThresholdRawValue);
- mPlausibilityThreshold = Settings.readPlausibilityThreshold(res);
- mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res);
- mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
- mCloudSyncEnabled = prefs.getBoolean(LocalSettingsConstants.PREF_ENABLE_CLOUD_SYNC, false);
- mAccount = prefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME,
- null /* default */);
- mGestureFloatingPreviewTextEnabled = !mInputAttributes.mDisableGestureFloatingPreviewText
- && prefs.getBoolean(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
- mAutoCorrectionEnabledPerUserSettings = mAutoCorrectEnabled
- && !mInputAttributes.mInputTypeNoAutoCorrect;
- mSuggestionsEnabledPerUserSettings = readSuggestionsEnabled(prefs);
- mIsInternal = Settings.isInternal(prefs);
- mHasCustomKeyPreviewAnimationParams = prefs.getBoolean(
- DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false);
- mHasKeyboardResize = prefs.getBoolean(DebugSettings.PREF_RESIZE_KEYBOARD, false);
- mKeyboardHeightScale = Settings.readKeyboardHeight(prefs, DEFAULT_SIZE_SCALE);
- mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration(
- prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
- res.getInteger(R.integer.config_key_preview_show_up_duration));
- mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration(
- prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
- res.getInteger(R.integer.config_key_preview_dismiss_duration));
- final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction(
- res, R.fraction.config_key_preview_show_up_start_scale);
- final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction(
- res, R.fraction.config_key_preview_dismiss_end_scale);
- mKeyPreviewShowUpStartXScale = Settings.readKeyPreviewAnimationScale(
- prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
- defaultKeyPreviewShowUpStartScale);
- mKeyPreviewShowUpStartYScale = Settings.readKeyPreviewAnimationScale(
- prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
- defaultKeyPreviewShowUpStartScale);
- mKeyPreviewDismissEndXScale = Settings.readKeyPreviewAnimationScale(
- prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
- defaultKeyPreviewDismissEndScale);
- mKeyPreviewDismissEndYScale = Settings.readKeyPreviewAnimationScale(
- prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
- defaultKeyPreviewDismissEndScale);
- mDisplayOrientation = res.getConfiguration().orientation;
- mAppWorkarounds = new AsyncResultHolder<>("AppWorkarounds");
- final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
- mInputAttributes.mTargetApplicationPackageName);
- if (null != packageInfo) {
- mAppWorkarounds.set(new AppWorkaroundsUtils(packageInfo));
- } else {
- new TargetPackageInfoGetterTask(context, mAppWorkarounds)
- .execute(mInputAttributes.mTargetApplicationPackageName);
- }
- }
-
- public boolean isMetricsLoggingEnabled() {
- return mEnableMetricsLogging;
- }
-
- public boolean isApplicationSpecifiedCompletionsOn() {
- return mInputAttributes.mApplicationSpecifiedCompletionOn;
- }
-
- public boolean needsToLookupSuggestions() {
- return mInputAttributes.mShouldShowSuggestions
- && (mAutoCorrectionEnabledPerUserSettings || isSuggestionsEnabledPerUserSettings());
- }
-
- public boolean isSuggestionsEnabledPerUserSettings() {
- return mSuggestionsEnabledPerUserSettings;
- }
-
- public boolean isPersonalizationEnabled() {
- return mUsePersonalizedDicts;
- }
-
- public boolean isWordSeparator(final int code) {
- return mSpacingAndPunctuations.isWordSeparator(code);
- }
-
- public boolean isWordConnector(final int code) {
- return mSpacingAndPunctuations.isWordConnector(code);
- }
-
- public boolean isWordCodePoint(final int code) {
- return Character.isLetter(code) || isWordConnector(code)
- || Character.COMBINING_SPACING_MARK == Character.getType(code);
- }
-
- public boolean isUsuallyPrecededBySpace(final int code) {
- return mSpacingAndPunctuations.isUsuallyPrecededBySpace(code);
- }
-
- public boolean isUsuallyFollowedBySpace(final int code) {
- return mSpacingAndPunctuations.isUsuallyFollowedBySpace(code);
- }
-
- public boolean shouldInsertSpacesAutomatically() {
- return mInputAttributes.mShouldInsertSpacesAutomatically;
- }
-
- public boolean isLanguageSwitchKeyEnabled() {
- if (!mShowsLanguageSwitchKey) {
- return false;
- }
- final RichInputMethodManager imm = RichInputMethodManager.getInstance();
- if (mIncludesOtherImesInLanguageSwitchList) {
- return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */);
- }
- return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */);
- }
-
- public boolean isSameInputType(final EditorInfo editorInfo) {
- return mInputAttributes.isSameInputType(editorInfo);
- }
-
- public boolean hasSameOrientation(final Configuration configuration) {
- return mDisplayOrientation == configuration.orientation;
- }
-
- public boolean isBeforeJellyBean() {
- final AppWorkaroundsUtils appWorkaroundUtils
- = mAppWorkarounds.get(null, TIMEOUT_TO_GET_TARGET_PACKAGE);
- return null == appWorkaroundUtils ? false : appWorkaroundUtils.isBeforeJellyBean();
- }
-
- public boolean isBrokenByRecorrection() {
- final AppWorkaroundsUtils appWorkaroundUtils
- = mAppWorkarounds.get(null, TIMEOUT_TO_GET_TARGET_PACKAGE);
- return null == appWorkaroundUtils ? false : appWorkaroundUtils.isBrokenByRecorrection();
- }
-
- private static final String SUGGESTIONS_VISIBILITY_HIDE_VALUE_OBSOLETE = "2";
-
- private static boolean readSuggestionsEnabled(final SharedPreferences prefs) {
- if (prefs.contains(Settings.PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE)) {
- final boolean alwaysHide = SUGGESTIONS_VISIBILITY_HIDE_VALUE_OBSOLETE.equals(
- prefs.getString(Settings.PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE, null));
- prefs.edit()
- .remove(Settings.PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE)
- .putBoolean(Settings.PREF_SHOW_SUGGESTIONS, !alwaysHide)
- .apply();
- }
- return prefs.getBoolean(Settings.PREF_SHOW_SUGGESTIONS, true);
- }
-
- private static boolean readBigramPredictionEnabled(final SharedPreferences prefs,
- final Resources res) {
- return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean(
- R.bool.config_default_next_word_prediction));
- }
-
- private static float readAutoCorrectionThreshold(final Resources res,
- final String currentAutoCorrectionSetting) {
- final String[] autoCorrectionThresholdValues = res.getStringArray(
- R.array.auto_correction_threshold_values);
- // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
- final float autoCorrectionThreshold;
- try {
- final int arrayIndex = Integer.parseInt(currentAutoCorrectionSetting);
- if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
- final String val = autoCorrectionThresholdValues[arrayIndex];
- if (FLOAT_MAX_VALUE_MARKER_STRING.equals(val)) {
- autoCorrectionThreshold = Float.MAX_VALUE;
- } else if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
- autoCorrectionThreshold = Float.NEGATIVE_INFINITY;
- } else {
- autoCorrectionThreshold = Float.parseFloat(val);
- }
- } else {
- autoCorrectionThreshold = Float.MAX_VALUE;
- }
- } catch (final NumberFormatException e) {
- // Whenever the threshold settings are correct, never come here.
- Log.w(TAG, "Cannot load auto correction threshold setting."
- + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
- + ", autoCorrectionThresholdValues: "
- + Arrays.toString(autoCorrectionThresholdValues), e);
- return Float.MAX_VALUE;
- }
- return autoCorrectionThreshold;
- }
-
- private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs,
- final Resources res) {
- // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
- // {@link Settings#PREF_VOICE_INPUT_KEY}.
- if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
- final String voiceModeMain = res.getString(R.string.voice_mode_main);
- final String voiceMode = prefs.getString(
- Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
- final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode);
- prefs.edit()
- .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey)
- // Remove the obsolete preference if exists.
- .remove(Settings.PREF_VOICE_MODE_OBSOLETE)
- .apply();
- }
- return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
- }
-
- public String dump() {
- final StringBuilder sb = new StringBuilder("Current settings :");
- sb.append("\n mSpacingAndPunctuations = ");
- sb.append("" + mSpacingAndPunctuations.dump());
- sb.append("\n mDelayInMillisecondsToUpdateOldSuggestions = ");
- sb.append("" + mDelayInMillisecondsToUpdateOldSuggestions);
- sb.append("\n mAutoCap = ");
- sb.append("" + mAutoCap);
- sb.append("\n mVibrateOn = ");
- sb.append("" + mVibrateOn);
- sb.append("\n mSoundOn = ");
- sb.append("" + mSoundOn);
- sb.append("\n mKeyPreviewPopupOn = ");
- sb.append("" + mKeyPreviewPopupOn);
- sb.append("\n mShowsVoiceInputKey = ");
- sb.append("" + mShowsVoiceInputKey);
- sb.append("\n mIncludesOtherImesInLanguageSwitchList = ");
- sb.append("" + mIncludesOtherImesInLanguageSwitchList);
- sb.append("\n mShowsLanguageSwitchKey = ");
- sb.append("" + mShowsLanguageSwitchKey);
- sb.append("\n mUseContactsDict = ");
- sb.append("" + mUseContactsDict);
- sb.append("\n mUsePersonalizedDicts = ");
- sb.append("" + mUsePersonalizedDicts);
- sb.append("\n mUseDoubleSpacePeriod = ");
- sb.append("" + mUseDoubleSpacePeriod);
- sb.append("\n mBlockPotentiallyOffensive = ");
- sb.append("" + mBlockPotentiallyOffensive);
- sb.append("\n mBigramPredictionEnabled = ");
- sb.append("" + mBigramPredictionEnabled);
- sb.append("\n mGestureInputEnabled = ");
- sb.append("" + mGestureInputEnabled);
- sb.append("\n mGestureTrailEnabled = ");
- sb.append("" + mGestureTrailEnabled);
- sb.append("\n mGestureFloatingPreviewTextEnabled = ");
- sb.append("" + mGestureFloatingPreviewTextEnabled);
- sb.append("\n mSlidingKeyInputPreviewEnabled = ");
- sb.append("" + mSlidingKeyInputPreviewEnabled);
- sb.append("\n mKeyLongpressTimeout = ");
- sb.append("" + mKeyLongpressTimeout);
- sb.append("\n mLocale = ");
- sb.append("" + mLocale);
- sb.append("\n mInputAttributes = ");
- sb.append("" + mInputAttributes);
- sb.append("\n mKeypressVibrationDuration = ");
- sb.append("" + mKeypressVibrationDuration);
- sb.append("\n mKeypressSoundVolume = ");
- sb.append("" + mKeypressSoundVolume);
- sb.append("\n mKeyPreviewPopupDismissDelay = ");
- sb.append("" + mKeyPreviewPopupDismissDelay);
- sb.append("\n mAutoCorrectEnabled = ");
- sb.append("" + mAutoCorrectEnabled);
- sb.append("\n mAutoCorrectionThreshold = ");
- sb.append("" + mAutoCorrectionThreshold);
- sb.append("\n mAutoCorrectionEnabledPerUserSettings = ");
- sb.append("" + mAutoCorrectionEnabledPerUserSettings);
- sb.append("\n mSuggestionsEnabledPerUserSettings = ");
- sb.append("" + mSuggestionsEnabledPerUserSettings);
- sb.append("\n mDisplayOrientation = ");
- sb.append("" + mDisplayOrientation);
- sb.append("\n mAppWorkarounds = ");
- final AppWorkaroundsUtils awu = mAppWorkarounds.get(null, 0);
- sb.append("" + (null == awu ? "null" : awu.toString()));
- sb.append("\n mIsInternal = ");
- sb.append("" + mIsInternal);
- sb.append("\n mKeyPreviewShowUpDuration = ");
- sb.append("" + mKeyPreviewShowUpDuration);
- sb.append("\n mKeyPreviewDismissDuration = ");
- sb.append("" + mKeyPreviewDismissDuration);
- sb.append("\n mKeyPreviewShowUpStartScaleX = ");
- sb.append("" + mKeyPreviewShowUpStartXScale);
- sb.append("\n mKeyPreviewShowUpStartScaleY = ");
- sb.append("" + mKeyPreviewShowUpStartYScale);
- sb.append("\n mKeyPreviewDismissEndScaleX = ");
- sb.append("" + mKeyPreviewDismissEndXScale);
- sb.append("\n mKeyPreviewDismissEndScaleY = ");
- sb.append("" + mKeyPreviewDismissEndYScale);
- return sb.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValuesForSuggestion.java b/java/src/com/android/inputmethod/latin/settings/SettingsValuesForSuggestion.java
deleted file mode 100644
index 5e2e5a5d6..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValuesForSuggestion.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-public class SettingsValuesForSuggestion {
- public final boolean mBlockPotentiallyOffensive;
-
- public SettingsValuesForSuggestion(final boolean blockPotentiallyOffensive) {
- mBlockPotentiallyOffensive = blockPotentiallyOffensive;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
deleted file mode 100644
index 70d97a5ba..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.res.Resources;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.internal.MoreKeySpec;
-import com.android.inputmethod.latin.PunctuationSuggestions;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.Arrays;
-import java.util.Locale;
-
-public final class SpacingAndPunctuations {
- private final int[] mSortedSymbolsPrecededBySpace;
- private final int[] mSortedSymbolsFollowedBySpace;
- private final int[] mSortedSymbolsClusteringTogether;
- private final int[] mSortedWordConnectors;
- public final int[] mSortedWordSeparators;
- public final PunctuationSuggestions mSuggestPuncList;
- private final int mSentenceSeparator;
- private final int mAbbreviationMarker;
- private final int[] mSortedSentenceTerminators;
- public final String mSentenceSeparatorAndSpace;
- public final boolean mCurrentLanguageHasSpaces;
- public final boolean mUsesAmericanTypography;
- public final boolean mUsesGermanRules;
-
- public SpacingAndPunctuations(final Resources res) {
- // To be able to binary search the code point. See {@link #isUsuallyPrecededBySpace(int)}.
- mSortedSymbolsPrecededBySpace = StringUtils.toSortedCodePointArray(
- res.getString(R.string.symbols_preceded_by_space));
- // To be able to binary search the code point. See {@link #isUsuallyFollowedBySpace(int)}.
- mSortedSymbolsFollowedBySpace = StringUtils.toSortedCodePointArray(
- res.getString(R.string.symbols_followed_by_space));
- mSortedSymbolsClusteringTogether = StringUtils.toSortedCodePointArray(
- res.getString(R.string.symbols_clustering_together));
- // To be able to binary search the code point. See {@link #isWordConnector(int)}.
- mSortedWordConnectors = StringUtils.toSortedCodePointArray(
- res.getString(R.string.symbols_word_connectors));
- mSortedWordSeparators = StringUtils.toSortedCodePointArray(
- res.getString(R.string.symbols_word_separators));
- mSortedSentenceTerminators = StringUtils.toSortedCodePointArray(
- res.getString(R.string.symbols_sentence_terminators));
- mSentenceSeparator = res.getInteger(R.integer.sentence_separator);
- mAbbreviationMarker = res.getInteger(R.integer.abbreviation_marker);
- mSentenceSeparatorAndSpace = new String(new int[] {
- mSentenceSeparator, Constants.CODE_SPACE }, 0, 2);
- mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces);
- final Locale locale = res.getConfiguration().locale;
- // Heuristic: we use American Typography rules because it's the most common rules for all
- // English variants. German rules (not "German typography") also have small gotchas.
- mUsesAmericanTypography = Locale.ENGLISH.getLanguage().equals(locale.getLanguage());
- mUsesGermanRules = Locale.GERMAN.getLanguage().equals(locale.getLanguage());
- final String[] suggestPuncsSpec = MoreKeySpec.splitKeySpecs(
- res.getString(R.string.suggested_punctuations));
- mSuggestPuncList = PunctuationSuggestions.newPunctuationSuggestions(suggestPuncsSpec);
- }
-
- @UsedForTesting
- public SpacingAndPunctuations(final SpacingAndPunctuations model,
- final int[] overrideSortedWordSeparators) {
- mSortedSymbolsPrecededBySpace = model.mSortedSymbolsPrecededBySpace;
- mSortedSymbolsFollowedBySpace = model.mSortedSymbolsFollowedBySpace;
- mSortedSymbolsClusteringTogether = model.mSortedSymbolsClusteringTogether;
- mSortedWordConnectors = model.mSortedWordConnectors;
- mSortedWordSeparators = overrideSortedWordSeparators;
- mSortedSentenceTerminators = model.mSortedSentenceTerminators;
- mSuggestPuncList = model.mSuggestPuncList;
- mSentenceSeparator = model.mSentenceSeparator;
- mAbbreviationMarker = model.mAbbreviationMarker;
- mSentenceSeparatorAndSpace = model.mSentenceSeparatorAndSpace;
- mCurrentLanguageHasSpaces = model.mCurrentLanguageHasSpaces;
- mUsesAmericanTypography = model.mUsesAmericanTypography;
- mUsesGermanRules = model.mUsesGermanRules;
- }
-
- public boolean isWordSeparator(final int code) {
- return Arrays.binarySearch(mSortedWordSeparators, code) >= 0;
- }
-
- public boolean isWordConnector(final int code) {
- return Arrays.binarySearch(mSortedWordConnectors, code) >= 0;
- }
-
- public boolean isWordCodePoint(final int code) {
- return Character.isLetter(code) || isWordConnector(code);
- }
-
- public boolean isUsuallyPrecededBySpace(final int code) {
- return Arrays.binarySearch(mSortedSymbolsPrecededBySpace, code) >= 0;
- }
-
- public boolean isUsuallyFollowedBySpace(final int code) {
- return Arrays.binarySearch(mSortedSymbolsFollowedBySpace, code) >= 0;
- }
-
- public boolean isClusteringSymbol(final int code) {
- return Arrays.binarySearch(mSortedSymbolsClusteringTogether, code) >= 0;
- }
-
- public boolean isSentenceTerminator(final int code) {
- return Arrays.binarySearch(mSortedSentenceTerminators, code) >= 0;
- }
-
- public boolean isAbbreviationMarker(final int code) {
- return code == mAbbreviationMarker;
- }
-
- public boolean isSentenceSeparator(final int code) {
- return code == mSentenceSeparator;
- }
-
- public String dump() {
- final StringBuilder sb = new StringBuilder();
- sb.append("mSortedSymbolsPrecededBySpace = ");
- sb.append("" + Arrays.toString(mSortedSymbolsPrecededBySpace));
- sb.append("\n mSortedSymbolsFollowedBySpace = ");
- sb.append("" + Arrays.toString(mSortedSymbolsFollowedBySpace));
- sb.append("\n mSortedWordConnectors = ");
- sb.append("" + Arrays.toString(mSortedWordConnectors));
- sb.append("\n mSortedWordSeparators = ");
- sb.append("" + Arrays.toString(mSortedWordSeparators));
- sb.append("\n mSuggestPuncList = ");
- sb.append("" + mSuggestPuncList);
- sb.append("\n mSentenceSeparator = ");
- sb.append("" + mSentenceSeparator);
- sb.append("\n mSentenceSeparatorAndSpace = ");
- sb.append("" + mSentenceSeparatorAndSpace);
- sb.append("\n mCurrentLanguageHasSpaces = ");
- sb.append("" + mCurrentLanguageHasSpaces);
- sb.append("\n mUsesAmericanTypography = ");
- sb.append("" + mUsesAmericanTypography);
- sb.append("\n mUsesGermanRules = ");
- sb.append("" + mUsesGermanRules);
- return sb.toString();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java b/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java
deleted file mode 100644
index 5994a76df..000000000
--- a/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.app.backup.BackupManager;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.util.Log;
-
-/**
- * A base abstract class for a {@link PreferenceFragment} that implements a nested
- * {@link PreferenceScreen} of the main preference screen.
- */
-public abstract class SubScreenFragment extends PreferenceFragment
- implements OnSharedPreferenceChangeListener {
- private OnSharedPreferenceChangeListener mSharedPreferenceChangeListener;
-
- static void setPreferenceEnabled(final String prefKey, final boolean enabled,
- final PreferenceScreen screen) {
- final Preference preference = screen.findPreference(prefKey);
- if (preference != null) {
- preference.setEnabled(enabled);
- }
- }
-
- static void removePreference(final String prefKey, final PreferenceScreen screen) {
- final Preference preference = screen.findPreference(prefKey);
- if (preference != null) {
- screen.removePreference(preference);
- }
- }
-
- static void updateListPreferenceSummaryToCurrentValue(final String prefKey,
- final PreferenceScreen screen) {
- // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before
- // KitKat, we need to update the summary programmatically.
- final ListPreference listPreference = (ListPreference)screen.findPreference(prefKey);
- if (listPreference == null) {
- return;
- }
- final CharSequence entries[] = listPreference.getEntries();
- final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue());
- listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]);
- }
-
- final void setPreferenceEnabled(final String prefKey, final boolean enabled) {
- setPreferenceEnabled(prefKey, enabled, getPreferenceScreen());
- }
-
- final void removePreference(final String prefKey) {
- removePreference(prefKey, getPreferenceScreen());
- }
-
- final void updateListPreferenceSummaryToCurrentValue(final String prefKey) {
- updateListPreferenceSummaryToCurrentValue(prefKey, getPreferenceScreen());
- }
-
- final SharedPreferences getSharedPreferences() {
- return getPreferenceManager().getSharedPreferences();
- }
-
- /**
- * Gets the application name to display on the UI.
- */
- final String getApplicationName() {
- final Context context = getActivity();
- final Resources res = getResources();
- final int applicationLabelRes = context.getApplicationInfo().labelRes;
- return res.getString(applicationLabelRes);
- }
-
- @Override
- public void addPreferencesFromResource(final int preferencesResId) {
- super.addPreferencesFromResource(preferencesResId);
- TwoStatePreferenceHelper.replaceCheckBoxPreferencesBySwitchPreferences(
- getPreferenceScreen());
- }
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mSharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- final SubScreenFragment fragment = SubScreenFragment.this;
- final Context context = fragment.getActivity();
- if (context == null || fragment.getPreferenceScreen() == null) {
- final String tag = fragment.getClass().getSimpleName();
- // TODO: Introduce a static function to register this class and ensure that
- // onCreate must be called before "onSharedPreferenceChanged" is called.
- Log.w(tag, "onSharedPreferenceChanged called before activity starts.");
- return;
- }
- new BackupManager(context).dataChanged();
- fragment.onSharedPreferenceChanged(prefs, key);
- }
- };
- getSharedPreferences().registerOnSharedPreferenceChangeListener(
- mSharedPreferenceChangeListener);
- }
-
- @Override
- public void onDestroy() {
- getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
- mSharedPreferenceChangeListener);
- super.onDestroy();
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- // This method may be overridden by an extended class.
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java b/java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java
deleted file mode 100644
index 254bc6567..000000000
--- a/java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * Test activity to use when testing preference fragments. <br/>
- * Usage: <br/>
- * Create an ActivityInstrumentationTestCase2 for this activity
- * and call setIntent() with an intent that specifies the fragment to load in the activity.
- * The fragment can then be obtained from this activity and used for testing/verification.
- */
-public final class TestFragmentActivity extends Activity {
- /**
- * The fragment name that should be loaded when starting this activity.
- * This must be specified when starting this activity, as this activity is only
- * meant to test fragments from instrumentation tests.
- */
- public static final String EXTRA_SHOW_FRAGMENT = "show_fragment";
-
- public Fragment mFragment;
-
- @Override
- protected void onCreate(final Bundle savedState) {
- super.onCreate(savedState);
- final Intent intent = getIntent();
- final String fragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
- if (fragmentName == null) {
- throw new IllegalArgumentException("No fragment name specified for testing");
- }
-
- mFragment = Fragment.instantiate(this, fragmentName);
- FragmentManager fragmentManager = getFragmentManager();
- fragmentManager.beginTransaction().add(mFragment, fragmentName).commit();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java
deleted file mode 100644
index 29289aed2..000000000
--- a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
-
-import com.android.inputmethod.keyboard.KeyboardTheme;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.settings.RadioButtonPreference.OnRadioButtonClickedListener;
-
-/**
- * "Keyboard theme" settings sub screen.
- */
-public final class ThemeSettingsFragment extends SubScreenFragment
- implements OnRadioButtonClickedListener {
- private int mSelectedThemeId;
-
- static class KeyboardThemePreference extends RadioButtonPreference {
- final int mThemeId;
-
- KeyboardThemePreference(final Context context, final String name, final int id) {
- super(context);
- setTitle(name);
- mThemeId = id;
- }
- }
-
- static void updateKeyboardThemeSummary(final Preference pref) {
- final Context context = pref.getContext();
- final Resources res = context.getResources();
- final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(context);
- final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names);
- final int[] keyboardThemeIds = res.getIntArray(R.array.keyboard_theme_ids);
- for (int index = 0; index < keyboardThemeNames.length; index++) {
- if (keyboardTheme.mThemeId == keyboardThemeIds[index]) {
- pref.setSummary(keyboardThemeNames[index]);
- return;
- }
- }
- }
-
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- addPreferencesFromResource(R.xml.prefs_screen_theme);
- final PreferenceScreen screen = getPreferenceScreen();
- final Context context = getActivity();
- final Resources res = getResources();
- final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names);
- final int[] keyboardThemeIds = res.getIntArray(R.array.keyboard_theme_ids);
- for (int index = 0; index < keyboardThemeNames.length; index++) {
- final KeyboardThemePreference pref = new KeyboardThemePreference(
- context, keyboardThemeNames[index], keyboardThemeIds[index]);
- screen.addPreference(pref);
- pref.setOnRadioButtonClickedListener(this);
- }
- final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(context);
- mSelectedThemeId = keyboardTheme.mThemeId;
- }
-
- @Override
- public void onRadioButtonClicked(final RadioButtonPreference preference) {
- if (preference instanceof KeyboardThemePreference) {
- final KeyboardThemePreference pref = (KeyboardThemePreference)preference;
- mSelectedThemeId = pref.mThemeId;
- updateSelected();
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- updateSelected();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- KeyboardTheme.saveKeyboardThemeId(mSelectedThemeId, getSharedPreferences());
- }
-
- private void updateSelected() {
- final PreferenceScreen screen = getPreferenceScreen();
- final int count = screen.getPreferenceCount();
- for (int index = 0; index < count; index++) {
- final Preference preference = screen.getPreference(index);
- if (preference instanceof KeyboardThemePreference) {
- final KeyboardThemePreference pref = (KeyboardThemePreference)preference;
- final boolean selected = (mSelectedThemeId == pref.mThemeId);
- pref.setSelected(selected);
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/TwoStatePreferenceHelper.java b/java/src/com/android/inputmethod/latin/settings/TwoStatePreferenceHelper.java
deleted file mode 100644
index 07a871ca0..000000000
--- a/java/src/com/android/inputmethod/latin/settings/TwoStatePreferenceHelper.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-import android.os.Build;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceGroup;
-import android.preference.SwitchPreference;
-
-import java.util.ArrayList;
-
-public class TwoStatePreferenceHelper {
- private static final String EMPTY_TEXT = "";
-
- private TwoStatePreferenceHelper() {
- // This utility class is not publicly instantiable.
- }
-
- public static void replaceCheckBoxPreferencesBySwitchPreferences(final PreferenceGroup group) {
- // The keyboard settings keeps using a CheckBoxPreference on KitKat or previous.
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- return;
- }
- // The keyboard settings starts using a SwitchPreference without switch on/off text on
- // API versions newer than KitKat.
- replaceAllCheckBoxPreferencesBySwitchPreferences(group);
- }
-
- private static void replaceAllCheckBoxPreferencesBySwitchPreferences(
- final PreferenceGroup group) {
- final ArrayList<Preference> preferences = new ArrayList<>();
- final int count = group.getPreferenceCount();
- for (int index = 0; index < count; index++) {
- preferences.add(group.getPreference(index));
- }
- group.removeAll();
- for (int index = 0; index < count; index++) {
- final Preference preference = preferences.get(index);
- if (preference instanceof CheckBoxPreference) {
- addSwitchPreferenceBasedOnCheckBoxPreference((CheckBoxPreference)preference, group);
- } else {
- group.addPreference(preference);
- if (preference instanceof PreferenceGroup) {
- replaceAllCheckBoxPreferencesBySwitchPreferences((PreferenceGroup)preference);
- }
- }
- }
- }
-
- static void addSwitchPreferenceBasedOnCheckBoxPreference(final CheckBoxPreference checkBox,
- final PreferenceGroup group) {
- final SwitchPreference switchPref = new SwitchPreference(checkBox.getContext());
- switchPref.setTitle(checkBox.getTitle());
- switchPref.setKey(checkBox.getKey());
- switchPref.setOrder(checkBox.getOrder());
- switchPref.setPersistent(checkBox.isPersistent());
- switchPref.setEnabled(checkBox.isEnabled());
- switchPref.setChecked(checkBox.isChecked());
- switchPref.setSummary(checkBox.getSummary());
- switchPref.setSummaryOn(checkBox.getSummaryOn());
- switchPref.setSummaryOff(checkBox.getSummaryOff());
- switchPref.setSwitchTextOn(EMPTY_TEXT);
- switchPref.setSwitchTextOff(EMPTY_TEXT);
- group.addPreference(switchPref);
- switchPref.setDependency(checkBox.getDependency());
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
deleted file mode 100644
index 7607429f8..000000000
--- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.setup;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-public final class SetupActivity extends Activity {
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Intent intent = new Intent();
- intent.setClass(this, SetupWizardActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- if (!isFinishing()) {
- finish();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
deleted file mode 100644
index 789694f08..000000000
--- a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.setup;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import androidx.core.view.ViewCompat;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.inputmethod.latin.R;
-
-public final class SetupStartIndicatorView extends LinearLayout {
- public SetupStartIndicatorView(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- setOrientation(HORIZONTAL);
- LayoutInflater.from(context).inflate(R.layout.setup_start_indicator_label, this);
-
- final LabelView labelView = (LabelView)findViewById(R.id.setup_start_label);
- labelView.setIndicatorView(findViewById(R.id.setup_start_indicator));
- }
-
- public static final class LabelView extends TextView {
- private View mIndicatorView;
-
- public LabelView(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setIndicatorView(final View indicatorView) {
- mIndicatorView = indicatorView;
- }
-
- // TODO: Once we stop supporting ICS, uncomment {@link #setPressed(boolean)} method and
- // remove this method.
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- for (final int state : getDrawableState()) {
- if (state == android.R.attr.state_pressed) {
- updateIndicatorView(true /* pressed */);
- return;
- }
- }
- updateIndicatorView(false /* pressed */);
- }
-
- // TODO: Once we stop supporting ICS, uncomment this method and remove
- // {@link #drawableStateChanged()} method.
-// @Override
-// public void setPressed(final boolean pressed) {
-// super.setPressed(pressed);
-// updateIndicatorView(pressed);
-// }
-
- private void updateIndicatorView(final boolean pressed) {
- if (mIndicatorView != null) {
- mIndicatorView.setPressed(pressed);
- mIndicatorView.invalidate();
- }
- }
- }
-
- public static final class IndicatorView extends View {
- private final Path mIndicatorPath = new Path();
- private final Paint mIndicatorPaint = new Paint();
- private final ColorStateList mIndicatorColor;
-
- public IndicatorView(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- mIndicatorColor = getResources().getColorStateList(
- R.color.setup_step_action_background);
- mIndicatorPaint.setStyle(Paint.Style.FILL);
- }
-
- @Override
- protected void onDraw(final Canvas canvas) {
- super.onDraw(canvas);
- final int layoutDirection = ViewCompat.getLayoutDirection(this);
- final int width = getWidth();
- final int height = getHeight();
- final float halfHeight = height / 2.0f;
- final Path path = mIndicatorPath;
- path.rewind();
- if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
- // Left arrow
- path.moveTo(width, 0.0f);
- path.lineTo(0.0f, halfHeight);
- path.lineTo(width, height);
- } else { // LAYOUT_DIRECTION_LTR
- // Right arrow
- path.moveTo(0.0f, 0.0f);
- path.lineTo(width, halfHeight);
- path.lineTo(0.0f, height);
- }
- path.close();
- final int[] stateSet = getDrawableState();
- final int color = mIndicatorColor.getColorForState(stateSet, 0);
- mIndicatorPaint.setColor(color);
- canvas.drawPath(path, mIndicatorPaint);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
deleted file mode 100644
index 9a39ceace..000000000
--- a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.setup;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import androidx.core.view.ViewCompat;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.inputmethod.latin.R;
-
-public final class SetupStepIndicatorView extends View {
- private final Path mIndicatorPath = new Path();
- private final Paint mIndicatorPaint = new Paint();
- private float mXRatio;
-
- public SetupStepIndicatorView(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- mIndicatorPaint.setColor(getResources().getColor(R.color.setup_step_background));
- mIndicatorPaint.setStyle(Paint.Style.FILL);
- }
-
- public void setIndicatorPosition(final int stepPos, final int totalStepNum) {
- final int layoutDirection = ViewCompat.getLayoutDirection(this);
- // The indicator position is the center of the partition that is equally divided into
- // the total step number.
- final float partionWidth = 1.0f / totalStepNum;
- final float pos = stepPos * partionWidth + partionWidth / 2.0f;
- mXRatio = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
- invalidate();
- }
-
- @Override
- protected void onDraw(final Canvas canvas) {
- super.onDraw(canvas);
- final int xPos = (int)(getWidth() * mXRatio);
- final int height = getHeight();
- mIndicatorPath.rewind();
- mIndicatorPath.moveTo(xPos, 0);
- mIndicatorPath.lineTo(xPos + height, height);
- mIndicatorPath.lineTo(xPos - height, height);
- mIndicatorPath.close();
- canvas.drawPath(mIndicatorPath, mIndicatorPaint);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
deleted file mode 100644
index bee22afd5..000000000
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.setup;
-
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Message;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.View;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.VideoView;
-
-import com.android.inputmethod.compat.TextViewCompatUtils;
-import com.android.inputmethod.compat.ViewCompatUtils;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.settings.SettingsActivity;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nonnull;
-
-// TODO: Use Fragment to implement welcome screen and setup steps.
-public final class SetupWizardActivity extends Activity implements View.OnClickListener {
- static final String TAG = SetupWizardActivity.class.getSimpleName();
-
- // For debugging purpose.
- private static final boolean FORCE_TO_SHOW_WELCOME_SCREEN = false;
- private static final boolean ENABLE_WELCOME_VIDEO = true;
-
- private InputMethodManager mImm;
-
- private View mSetupWizard;
- private View mWelcomeScreen;
- private View mSetupScreen;
- private Uri mWelcomeVideoUri;
- private VideoView mWelcomeVideoView;
- private ImageView mWelcomeImageView;
- private View mActionStart;
- private View mActionNext;
- private TextView mStep1Bullet;
- private TextView mActionFinish;
- private SetupStepGroup mSetupStepGroup;
- private static final String STATE_STEP = "step";
- private int mStepNumber;
- private boolean mNeedsToAdjustStepNumberToSystemState;
- private static final int STEP_WELCOME = 0;
- private static final int STEP_1 = 1;
- private static final int STEP_2 = 2;
- private static final int STEP_3 = 3;
- private static final int STEP_LAUNCHING_IME_SETTINGS = 4;
- private static final int STEP_BACK_FROM_IME_SETTINGS = 5;
-
- private SettingsPoolingHandler mHandler;
-
- private static final class SettingsPoolingHandler
- extends LeakGuardHandlerWrapper<SetupWizardActivity> {
- private static final int MSG_POLLING_IME_SETTINGS = 0;
- private static final long IME_SETTINGS_POLLING_INTERVAL = 200;
-
- private final InputMethodManager mImmInHandler;
-
- public SettingsPoolingHandler(@Nonnull final SetupWizardActivity ownerInstance,
- final InputMethodManager imm) {
- super(ownerInstance);
- mImmInHandler = imm;
- }
-
- @Override
- public void handleMessage(final Message msg) {
- final SetupWizardActivity setupWizardActivity = getOwnerInstance();
- if (setupWizardActivity == null) {
- return;
- }
- switch (msg.what) {
- case MSG_POLLING_IME_SETTINGS:
- if (UncachedInputMethodManagerUtils.isThisImeEnabled(setupWizardActivity,
- mImmInHandler)) {
- setupWizardActivity.invokeSetupWizardOfThisIme();
- return;
- }
- startPollingImeSettings();
- break;
- }
- }
-
- public void startPollingImeSettings() {
- sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS),
- IME_SETTINGS_POLLING_INTERVAL);
- }
-
- public void cancelPollingImeSettings() {
- removeMessages(MSG_POLLING_IME_SETTINGS);
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- setTheme(android.R.style.Theme_Translucent_NoTitleBar);
- super.onCreate(savedInstanceState);
-
- mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
- mHandler = new SettingsPoolingHandler(this, mImm);
-
- setContentView(R.layout.setup_wizard);
- mSetupWizard = findViewById(R.id.setup_wizard);
-
- if (savedInstanceState == null) {
- mStepNumber = determineSetupStepNumberFromLauncher();
- } else {
- mStepNumber = savedInstanceState.getInt(STATE_STEP);
- }
-
- final String applicationName = getResources().getString(getApplicationInfo().labelRes);
- mWelcomeScreen = findViewById(R.id.setup_welcome_screen);
- final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title);
- welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName));
-
- mSetupScreen = findViewById(R.id.setup_steps_screen);
- final TextView stepsTitle = (TextView)findViewById(R.id.setup_title);
- stepsTitle.setText(getString(R.string.setup_steps_title, applicationName));
-
- final SetupStepIndicatorView indicatorView =
- (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator);
- mSetupStepGroup = new SetupStepGroup(indicatorView);
-
- mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet);
- mStep1Bullet.setOnClickListener(this);
- final SetupStep step1 = new SetupStep(STEP_1, applicationName,
- mStep1Bullet, findViewById(R.id.setup_step1),
- R.string.setup_step1_title, R.string.setup_step1_instruction,
- R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1,
- R.string.setup_step1_action);
- final SettingsPoolingHandler handler = mHandler;
- step1.setAction(new Runnable() {
- @Override
- public void run() {
- invokeLanguageAndInputSettings();
- handler.startPollingImeSettings();
- }
- });
- mSetupStepGroup.addStep(step1);
-
- final SetupStep step2 = new SetupStep(STEP_2, applicationName,
- (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2),
- R.string.setup_step2_title, R.string.setup_step2_instruction,
- 0 /* finishedInstruction */, R.drawable.ic_setup_step2,
- R.string.setup_step2_action);
- step2.setAction(new Runnable() {
- @Override
- public void run() {
- invokeInputMethodPicker();
- }
- });
- mSetupStepGroup.addStep(step2);
-
- final SetupStep step3 = new SetupStep(STEP_3, applicationName,
- (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3),
- R.string.setup_step3_title, R.string.setup_step3_instruction,
- 0 /* finishedInstruction */, R.drawable.ic_setup_step3,
- R.string.setup_step3_action);
- step3.setAction(new Runnable() {
- @Override
- public void run() {
- invokeSubtypeEnablerOfThisIme();
- }
- });
- mSetupStepGroup.addStep(step3);
-
- mWelcomeVideoUri = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(getPackageName())
- .path(Integer.toString(R.raw.setup_welcome_video))
- .build();
- final VideoView welcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video);
- welcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
- @Override
- public void onPrepared(final MediaPlayer mp) {
- // Now VideoView has been laid-out and ready to play, remove background of it to
- // reveal the video.
- welcomeVideoView.setBackgroundResource(0);
- mp.setLooping(true);
- }
- });
- welcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- @Override
- public boolean onError(final MediaPlayer mp, final int what, final int extra) {
- Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra);
- hideWelcomeVideoAndShowWelcomeImage();
- return true;
- }
- });
- mWelcomeVideoView = welcomeVideoView;
- mWelcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image);
-
- mActionStart = findViewById(R.id.setup_start_label);
- mActionStart.setOnClickListener(this);
- mActionNext = findViewById(R.id.setup_next);
- mActionNext.setOnClickListener(this);
- mActionFinish = (TextView)findViewById(R.id.setup_finish);
- TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish,
- getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null);
- mActionFinish.setOnClickListener(this);
- }
-
- @Override
- public void onClick(final View v) {
- if (v == mActionFinish) {
- finish();
- return;
- }
- final int currentStep = determineSetupStepNumber();
- final int nextStep;
- if (v == mActionStart) {
- nextStep = STEP_1;
- } else if (v == mActionNext) {
- nextStep = mStepNumber + 1;
- } else if (v == mStep1Bullet && currentStep == STEP_2) {
- nextStep = STEP_1;
- } else {
- nextStep = mStepNumber;
- }
- if (mStepNumber != nextStep) {
- mStepNumber = nextStep;
- updateSetupStepView();
- }
- }
-
- void invokeSetupWizardOfThisIme() {
- final Intent intent = new Intent();
- intent.setClass(this, SetupWizardActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- mNeedsToAdjustStepNumberToSystemState = true;
- }
-
- private void invokeSettingsOfThisIme() {
- final Intent intent = new Intent();
- intent.setClass(this, SettingsActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(SettingsActivity.EXTRA_ENTRY_KEY,
- SettingsActivity.EXTRA_ENTRY_VALUE_APP_ICON);
- startActivity(intent);
- }
-
- void invokeLanguageAndInputSettings() {
- final Intent intent = new Intent();
- intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- startActivity(intent);
- mNeedsToAdjustStepNumberToSystemState = true;
- }
-
- void invokeInputMethodPicker() {
- // Invoke input method picker.
- mImm.showInputMethodPicker();
- mNeedsToAdjustStepNumberToSystemState = true;
- }
-
- void invokeSubtypeEnablerOfThisIme() {
- final InputMethodInfo imi =
- UncachedInputMethodManagerUtils.getInputMethodInfoOf(getPackageName(), mImm);
- if (imi == null) {
- return;
- }
- final Intent intent = new Intent();
- intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId());
- startActivity(intent);
- }
-
- private int determineSetupStepNumberFromLauncher() {
- final int stepNumber = determineSetupStepNumber();
- if (stepNumber == STEP_1) {
- return STEP_WELCOME;
- }
- if (stepNumber == STEP_3) {
- return STEP_LAUNCHING_IME_SETTINGS;
- }
- return stepNumber;
- }
-
- private int determineSetupStepNumber() {
- mHandler.cancelPollingImeSettings();
- if (FORCE_TO_SHOW_WELCOME_SCREEN) {
- return STEP_1;
- }
- if (!UncachedInputMethodManagerUtils.isThisImeEnabled(this, mImm)) {
- return STEP_1;
- }
- if (!UncachedInputMethodManagerUtils.isThisImeCurrent(this, mImm)) {
- return STEP_2;
- }
- return STEP_3;
- }
-
- @Override
- protected void onSaveInstanceState(final Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(STATE_STEP, mStepNumber);
- }
-
- @Override
- protected void onRestoreInstanceState(final Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- mStepNumber = savedInstanceState.getInt(STATE_STEP);
- }
-
- private static boolean isInSetupSteps(final int stepNumber) {
- return stepNumber >= STEP_1 && stepNumber <= STEP_3;
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- // Probably the setup wizard has been invoked from "Recent" menu. The setup step number
- // needs to be adjusted to system state, because the state (IME is enabled and/or current)
- // may have been changed.
- if (isInSetupSteps(mStepNumber)) {
- mStepNumber = determineSetupStepNumber();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (mStepNumber == STEP_LAUNCHING_IME_SETTINGS) {
- // Prevent white screen flashing while launching settings activity.
- mSetupWizard.setVisibility(View.INVISIBLE);
- invokeSettingsOfThisIme();
- mStepNumber = STEP_BACK_FROM_IME_SETTINGS;
- return;
- }
- if (mStepNumber == STEP_BACK_FROM_IME_SETTINGS) {
- finish();
- return;
- }
- updateSetupStepView();
- }
-
- @Override
- public void onBackPressed() {
- if (mStepNumber == STEP_1) {
- mStepNumber = STEP_WELCOME;
- updateSetupStepView();
- return;
- }
- super.onBackPressed();
- }
-
- void hideWelcomeVideoAndShowWelcomeImage() {
- mWelcomeVideoView.setVisibility(View.GONE);
- mWelcomeImageView.setImageResource(R.raw.setup_welcome_image);
- mWelcomeImageView.setVisibility(View.VISIBLE);
- }
-
- private void showAndStartWelcomeVideo() {
- mWelcomeVideoView.setVisibility(View.VISIBLE);
- mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
- mWelcomeVideoView.start();
- }
-
- private void hideAndStopWelcomeVideo() {
- mWelcomeVideoView.stopPlayback();
- mWelcomeVideoView.setVisibility(View.GONE);
- }
-
- @Override
- protected void onPause() {
- hideAndStopWelcomeVideo();
- super.onPause();
- }
-
- @Override
- public void onWindowFocusChanged(final boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (hasFocus && mNeedsToAdjustStepNumberToSystemState) {
- mNeedsToAdjustStepNumberToSystemState = false;
- mStepNumber = determineSetupStepNumber();
- updateSetupStepView();
- }
- }
-
- private void updateSetupStepView() {
- mSetupWizard.setVisibility(View.VISIBLE);
- final boolean welcomeScreen = (mStepNumber == STEP_WELCOME);
- mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE);
- mSetupScreen.setVisibility(welcomeScreen ? View.GONE : View.VISIBLE);
- if (welcomeScreen) {
- if (ENABLE_WELCOME_VIDEO) {
- showAndStartWelcomeVideo();
- } else {
- hideWelcomeVideoAndShowWelcomeImage();
- }
- return;
- }
- hideAndStopWelcomeVideo();
- final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber();
- mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone);
- mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE);
- mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE);
- }
-
- static final class SetupStep implements View.OnClickListener {
- public final int mStepNo;
- private final View mStepView;
- private final TextView mBulletView;
- private final int mActivatedColor;
- private final int mDeactivatedColor;
- private final String mInstruction;
- private final String mFinishedInstruction;
- private final TextView mActionLabel;
- private Runnable mAction;
-
- public SetupStep(final int stepNo, final String applicationName, final TextView bulletView,
- final View stepView, final int title, final int instruction,
- final int finishedInstruction, final int actionIcon, final int actionLabel) {
- mStepNo = stepNo;
- mStepView = stepView;
- mBulletView = bulletView;
- final Resources res = stepView.getResources();
- mActivatedColor = res.getColor(R.color.setup_text_action);
- mDeactivatedColor = res.getColor(R.color.setup_text_dark);
-
- final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title);
- titleView.setText(res.getString(title, applicationName));
- mInstruction = (instruction == 0) ? null
- : res.getString(instruction, applicationName);
- mFinishedInstruction = (finishedInstruction == 0) ? null
- : res.getString(finishedInstruction, applicationName);
-
- mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label);
- mActionLabel.setText(res.getString(actionLabel));
- if (actionIcon == 0) {
- final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel);
- ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
- } else {
- TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
- mActionLabel, res.getDrawable(actionIcon), null, null, null);
- }
- }
-
- public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) {
- mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE);
- mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor);
- final TextView instructionView = (TextView)mStepView.findViewById(
- R.id.setup_step_instruction);
- instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction);
- mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE);
- }
-
- public void setAction(final Runnable action) {
- mActionLabel.setOnClickListener(this);
- mAction = action;
- }
-
- @Override
- public void onClick(final View v) {
- if (v == mActionLabel && mAction != null) {
- mAction.run();
- return;
- }
- }
- }
-
- static final class SetupStepGroup {
- private final SetupStepIndicatorView mIndicatorView;
- private final ArrayList<SetupStep> mGroup = new ArrayList<>();
-
- public SetupStepGroup(final SetupStepIndicatorView indicatorView) {
- mIndicatorView = indicatorView;
- }
-
- public void addStep(final SetupStep step) {
- mGroup.add(step);
- }
-
- public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) {
- for (final SetupStep step : mGroup) {
- step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone);
- }
- mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size());
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
deleted file mode 100644
index 4625e8e8b..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.service.textservice.SpellCheckerService;
-import android.text.InputType;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodSubtype;
-import android.view.textservice.SuggestionsInfo;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
-import com.android.inputmethod.latin.DictionaryFacilitator;
-import com.android.inputmethod.latin.DictionaryFacilitatorLruCache;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.common.ComposedData;
-import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
-import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.ScriptUtils;
-import com.android.inputmethod.latin.utils.SuggestionResults;
-
-import java.util.Locale;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Semaphore;
-
-import javax.annotation.Nonnull;
-
-/**
- * Service for spell checking, using LatinIME's dictionaries and mechanisms.
- */
-public final class AndroidSpellCheckerService extends SpellCheckerService
- implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = AndroidSpellCheckerService.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts";
-
- private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480;
- private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 301;
-
- private static final String DICTIONARY_NAME_PREFIX = "spellcheck_";
-
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- private final int MAX_NUM_OF_THREADS_READ_DICTIONARY = 2;
- private final Semaphore mSemaphore = new Semaphore(MAX_NUM_OF_THREADS_READ_DICTIONARY,
- true /* fair */);
- // TODO: Make each spell checker session has its own session id.
- private final ConcurrentLinkedQueue<Integer> mSessionIdPool = new ConcurrentLinkedQueue<>();
-
- private final DictionaryFacilitatorLruCache mDictionaryFacilitatorCache =
- new DictionaryFacilitatorLruCache(this /* context */, DICTIONARY_NAME_PREFIX);
- private final ConcurrentHashMap<Locale, Keyboard> mKeyboardCache = new ConcurrentHashMap<>();
-
- // The threshold for a suggestion to be considered "recommended".
- private float mRecommendedThreshold;
- // TODO: make a spell checker option to block offensive words or not
- private final SettingsValuesForSuggestion mSettingsValuesForSuggestion =
- new SettingsValuesForSuggestion(true /* blockPotentiallyOffensive */);
-
- public static final String SINGLE_QUOTE = "\u0027";
- public static final String APOSTROPHE = "\u2019";
-
- public AndroidSpellCheckerService() {
- super();
- for (int i = 0; i < MAX_NUM_OF_THREADS_READ_DICTIONARY; i++) {
- mSessionIdPool.add(i);
- }
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mRecommendedThreshold = Float.parseFloat(
- getString(R.string.spellchecker_recommended_threshold_value));
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- prefs.registerOnSharedPreferenceChangeListener(this);
- onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
- }
-
- public float getRecommendedThreshold() {
- return mRecommendedThreshold;
- }
-
- private static String getKeyboardLayoutNameForLocale(final Locale locale) {
- // See b/19963288.
- if (locale.getLanguage().equals("sr")) {
- return "south_slavic";
- }
- final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale);
- switch (script) {
- case ScriptUtils.SCRIPT_LATIN:
- return "qwerty";
- case ScriptUtils.SCRIPT_CYRILLIC:
- return "east_slavic";
- case ScriptUtils.SCRIPT_GREEK:
- return "greek";
- case ScriptUtils.SCRIPT_HEBREW:
- return "hebrew";
- default:
- throw new RuntimeException("Wrong script supplied: " + script);
- }
- }
-
- @Override
- public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- if (!PREF_USE_CONTACTS_KEY.equals(key)) return;
- final boolean useContactsDictionary = prefs.getBoolean(PREF_USE_CONTACTS_KEY, true);
- mDictionaryFacilitatorCache.setUseContactsDictionary(useContactsDictionary);
- }
-
- @Override
- public Session createSession() {
- // Should not refer to AndroidSpellCheckerSession directly considering
- // that AndroidSpellCheckerSession may be overlaid.
- return AndroidSpellCheckerSessionFactory.newInstance(this);
- }
-
- /**
- * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary.
- * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline.
- * @return the empty SuggestionsInfo with the appropriate flags set.
- */
- public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) {
- return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0,
- EMPTY_STRING_ARRAY);
- }
-
- /**
- * Returns an empty suggestionInfo with flags signaling the word is in the dictionary.
- * @return the empty SuggestionsInfo with the appropriate flags set.
- */
- public static SuggestionsInfo getInDictEmptySuggestions() {
- return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
- EMPTY_STRING_ARRAY);
- }
-
- public boolean isValidWord(final Locale locale, final String word) {
- mSemaphore.acquireUninterruptibly();
- try {
- DictionaryFacilitator dictionaryFacilitatorForLocale =
- mDictionaryFacilitatorCache.get(locale);
- return dictionaryFacilitatorForLocale.isValidSpellingWord(word);
- } finally {
- mSemaphore.release();
- }
- }
-
- public SuggestionResults getSuggestionResults(final Locale locale,
- final ComposedData composedData, final NgramContext ngramContext,
- @Nonnull final Keyboard keyboard) {
- Integer sessionId = null;
- mSemaphore.acquireUninterruptibly();
- try {
- sessionId = mSessionIdPool.poll();
- DictionaryFacilitator dictionaryFacilitatorForLocale =
- mDictionaryFacilitatorCache.get(locale);
- return dictionaryFacilitatorForLocale.getSuggestionResults(composedData, ngramContext,
- keyboard, mSettingsValuesForSuggestion,
- sessionId, SuggestedWords.INPUT_STYLE_TYPING);
- } finally {
- if (sessionId != null) {
- mSessionIdPool.add(sessionId);
- }
- mSemaphore.release();
- }
- }
-
- public boolean hasMainDictionaryForLocale(final Locale locale) {
- mSemaphore.acquireUninterruptibly();
- try {
- final DictionaryFacilitator dictionaryFacilitator =
- mDictionaryFacilitatorCache.get(locale);
- return dictionaryFacilitator.hasAtLeastOneInitializedMainDictionary();
- } finally {
- mSemaphore.release();
- }
- }
-
- @Override
- public boolean onUnbind(final Intent intent) {
- mSemaphore.acquireUninterruptibly(MAX_NUM_OF_THREADS_READ_DICTIONARY);
- try {
- mDictionaryFacilitatorCache.closeDictionaries();
- } finally {
- mSemaphore.release(MAX_NUM_OF_THREADS_READ_DICTIONARY);
- }
- mKeyboardCache.clear();
- return false;
- }
-
- public Keyboard getKeyboardForLocale(final Locale locale) {
- Keyboard keyboard = mKeyboardCache.get(locale);
- if (keyboard == null) {
- keyboard = createKeyboardForLocale(locale);
- if (keyboard != null) {
- mKeyboardCache.put(locale, keyboard);
- }
- }
- return keyboard;
- }
-
- private Keyboard createKeyboardForLocale(final Locale locale) {
- final String keyboardLayoutName = getKeyboardLayoutNameForLocale(locale);
- final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype(
- locale.toString(), keyboardLayoutName);
- final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype);
- return keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
- }
-
- private KeyboardLayoutSet createKeyboardSetForSpellChecker(final InputMethodSubtype subtype) {
- final EditorInfo editorInfo = new EditorInfo();
- editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
- final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(this, editorInfo);
- builder.setKeyboardGeometry(
- SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT);
- builder.setSubtype(RichInputMethodSubtype.getRichInputMethodSubtype(subtype));
- builder.setIsSpellChecker(true /* isSpellChecker */);
- builder.disableTouchPositionCorrectionData();
- return builder.build();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
deleted file mode 100644
index c7622e7a1..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.annotation.TargetApi;
-import android.content.res.Resources;
-import android.os.Binder;
-import android.os.Build;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.textservice.SentenceSuggestionsInfo;
-import android.view.textservice.SuggestionsInfo;
-import android.view.textservice.TextInfo;
-
-import com.android.inputmethod.compat.TextInfoCompatUtils;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.utils.SpannableStringUtils;
-
-import java.util.ArrayList;
-import java.util.Locale;
-
-public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
- private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
- private static final boolean DBG = false;
- private final Resources mResources;
- private SentenceLevelAdapter mSentenceLevelAdapter;
-
- public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
- super(service);
- mResources = service.getResources();
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
- SentenceSuggestionsInfo ssi) {
- final CharSequence typedText = TextInfoCompatUtils.getCharSequenceOrString(ti);
- if (!typedText.toString().contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
- return null;
- }
- final int N = ssi.getSuggestionsCount();
- final ArrayList<Integer> additionalOffsets = new ArrayList<>();
- final ArrayList<Integer> additionalLengths = new ArrayList<>();
- final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = new ArrayList<>();
- CharSequence currentWord = null;
- for (int i = 0; i < N; ++i) {
- final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
- final int flags = si.getSuggestionsAttributes();
- if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
- continue;
- }
- final int offset = ssi.getOffsetAt(i);
- final int length = ssi.getLengthAt(i);
- final CharSequence subText = typedText.subSequence(offset, offset + length);
- final NgramContext ngramContext =
- new NgramContext(new NgramContext.WordInfo(currentWord));
- currentWord = subText;
- if (!subText.toString().contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
- continue;
- }
- // Split preserving spans.
- final CharSequence[] splitTexts = SpannableStringUtils.split(subText,
- AndroidSpellCheckerService.SINGLE_QUOTE,
- true /* preserveTrailingEmptySegments */);
- if (splitTexts == null || splitTexts.length <= 1) {
- continue;
- }
- final int splitNum = splitTexts.length;
- for (int j = 0; j < splitNum; ++j) {
- final CharSequence splitText = splitTexts[j];
- if (TextUtils.isEmpty(splitText)) {
- continue;
- }
- if (mSuggestionsCache.getSuggestionsFromCache(splitText.toString()) == null) {
- continue;
- }
- final int newLength = splitText.length();
- // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
- final int newFlags = 0;
- final SuggestionsInfo newSi = new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
- newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
- if (DBG) {
- Log.d(TAG, "Override and remove old span over: " + splitText + ", "
- + offset + "," + newLength);
- }
- additionalOffsets.add(offset);
- additionalLengths.add(newLength);
- additionalSuggestionsInfos.add(newSi);
- }
- }
- final int additionalSize = additionalOffsets.size();
- if (additionalSize <= 0) {
- return null;
- }
- final int suggestionsSize = N + additionalSize;
- final int[] newOffsets = new int[suggestionsSize];
- final int[] newLengths = new int[suggestionsSize];
- final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
- int i;
- for (i = 0; i < N; ++i) {
- newOffsets[i] = ssi.getOffsetAt(i);
- newLengths[i] = ssi.getLengthAt(i);
- newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
- }
- for (; i < suggestionsSize; ++i) {
- newOffsets[i] = additionalOffsets.get(i - N);
- newLengths[i] = additionalLengths.get(i - N);
- newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
- }
- return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
- }
-
- @Override
- public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
- int suggestionsLimit) {
- final SentenceSuggestionsInfo[] retval = splitAndSuggest(textInfos, suggestionsLimit);
- if (retval == null || retval.length != textInfos.length) {
- return retval;
- }
- for (int i = 0; i < retval.length; ++i) {
- final SentenceSuggestionsInfo tempSsi =
- fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
- if (tempSsi != null) {
- retval[i] = tempSsi;
- }
- }
- return retval;
- }
-
- /**
- * Get sentence suggestions for specified texts in an array of TextInfo. This is taken from
- * SpellCheckerService#onGetSentenceSuggestionsMultiple that we can't use because it's
- * using private variables.
- * The default implementation splits the input text to words and returns
- * {@link SentenceSuggestionsInfo} which contains suggestions for each word.
- * This function will run on the incoming IPC thread.
- * So, this is not called on the main thread,
- * but will be called in series on another thread.
- * @param textInfos an array of the text metadata
- * @param suggestionsLimit the maximum number of suggestions to be returned
- * @return an array of {@link SentenceSuggestionsInfo} returned by
- * {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
- */
- private SentenceSuggestionsInfo[] splitAndSuggest(TextInfo[] textInfos, int suggestionsLimit) {
- if (textInfos == null || textInfos.length == 0) {
- return SentenceLevelAdapter.getEmptySentenceSuggestionsInfo();
- }
- SentenceLevelAdapter sentenceLevelAdapter;
- synchronized(this) {
- sentenceLevelAdapter = mSentenceLevelAdapter;
- if (sentenceLevelAdapter == null) {
- final String localeStr = getLocale();
- if (!TextUtils.isEmpty(localeStr)) {
- sentenceLevelAdapter = new SentenceLevelAdapter(mResources,
- new Locale(localeStr));
- mSentenceLevelAdapter = sentenceLevelAdapter;
- }
- }
- }
- if (sentenceLevelAdapter == null) {
- return SentenceLevelAdapter.getEmptySentenceSuggestionsInfo();
- }
- final int infosSize = textInfos.length;
- final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize];
- for (int i = 0; i < infosSize; ++i) {
- final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams =
- sentenceLevelAdapter.getSplitWords(textInfos[i]);
- final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems =
- textInfoParams.mItems;
- final int itemsSize = mItems.size();
- final TextInfo[] splitTextInfos = new TextInfo[itemsSize];
- for (int j = 0; j < itemsSize; ++j) {
- splitTextInfos[j] = mItems.get(j).mTextInfo;
- }
- retval[i] = SentenceLevelAdapter.reconstructSuggestions(
- textInfoParams, onGetSuggestionsMultiple(
- splitTextInfos, suggestionsLimit, true));
- }
- return retval;
- }
-
- @Override
- public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
- int suggestionsLimit, boolean sequentialWords) {
- long ident = Binder.clearCallingIdentity();
- try {
- final int length = textInfos.length;
- final SuggestionsInfo[] retval = new SuggestionsInfo[length];
- for (int i = 0; i < length; ++i) {
- final CharSequence prevWord;
- if (sequentialWords && i > 0) {
- final TextInfo prevTextInfo = textInfos[i - 1];
- final CharSequence prevWordCandidate =
- TextInfoCompatUtils.getCharSequenceOrString(prevTextInfo);
- // Note that an empty string would be used to indicate the initial word
- // in the future.
- prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
- } else {
- prevWord = null;
- }
- final NgramContext ngramContext =
- new NgramContext(new NgramContext.WordInfo(prevWord));
- final TextInfo textInfo = textInfos[i];
- retval[i] = onGetSuggestionsInternal(textInfo, ngramContext, suggestionsLimit);
- retval[i].setCookieAndSequence(textInfo.getCookie(), textInfo.getSequence());
- }
- return retval;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java
deleted file mode 100644
index e0418d404..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.service.textservice.SpellCheckerService.Session;
-
-public abstract class AndroidSpellCheckerSessionFactory {
- public static Session newInstance(AndroidSpellCheckerService service) {
- return new AndroidSpellCheckerSession(service);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
deleted file mode 100644
index 9223923a7..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.os.Binder;
-import android.provider.UserDictionary.Words;
-import android.service.textservice.SpellCheckerService.Session;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.LruCache;
-import android.view.textservice.SuggestionsInfo;
-import android.view.textservice.TextInfo;
-
-import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.ScriptUtils;
-import com.android.inputmethod.latin.utils.StatsUtils;
-import com.android.inputmethod.latin.utils.SuggestionResults;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-public abstract class AndroidWordLevelSpellCheckerSession extends Session {
- private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName();
-
- public final static String[] EMPTY_STRING_ARRAY = new String[0];
-
- // Immutable, but not available in the constructor.
- private Locale mLocale;
- // Cache this for performance
- private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now.
- private final AndroidSpellCheckerService mService;
- protected final SuggestionsCache mSuggestionsCache = new SuggestionsCache();
- private final ContentObserver mObserver;
-
- private static final String quotesRegexp =
- "(\\u0022|\\u0027|\\u0060|\\u00B4|\\u2018|\\u2018|\\u201C|\\u201D)";
-
- private static final class SuggestionsParams {
- public final String[] mSuggestions;
- public final int mFlags;
- public SuggestionsParams(String[] suggestions, int flags) {
- mSuggestions = suggestions;
- mFlags = flags;
- }
- }
-
- protected static final class SuggestionsCache {
- private static final int MAX_CACHE_SIZE = 50;
- private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache =
- new LruCache<>(MAX_CACHE_SIZE);
-
- private static String generateKey(final String query) {
- return query + "";
- }
-
- public SuggestionsParams getSuggestionsFromCache(final String query) {
- return mUnigramSuggestionsInfoCache.get(query);
- }
-
- public void putSuggestionsToCache(
- final String query, final String[] suggestions, final int flags) {
- if (suggestions == null || TextUtils.isEmpty(query)) {
- return;
- }
- mUnigramSuggestionsInfoCache.put(
- generateKey(query),
- new SuggestionsParams(suggestions, flags));
- }
-
- public void clearCache() {
- mUnigramSuggestionsInfoCache.evictAll();
- }
- }
-
- AndroidWordLevelSpellCheckerSession(final AndroidSpellCheckerService service) {
- mService = service;
- final ContentResolver cres = service.getContentResolver();
-
- mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean self) {
- mSuggestionsCache.clearCache();
- }
- };
- cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
- }
-
- @Override
- public void onCreate() {
- final String localeString = getLocale();
- mLocale = (null == localeString) ? null
- : LocaleUtils.constructLocaleFromString(localeString);
- mScript = ScriptUtils.getScriptFromSpellCheckerLocale(mLocale);
- }
-
- @Override
- public void onClose() {
- final ContentResolver cres = mService.getContentResolver();
- cres.unregisterContentObserver(mObserver);
- }
-
- private static final int CHECKABILITY_CHECKABLE = 0;
- private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1;
- private static final int CHECKABILITY_CONTAINS_PERIOD = 2;
- private static final int CHECKABILITY_EMAIL_OR_URL = 3;
- private static final int CHECKABILITY_FIRST_LETTER_UNCHECKABLE = 4;
- private static final int CHECKABILITY_TOO_SHORT = 5;
- /**
- * Finds out whether a particular string should be filtered out of spell checking.
- *
- * This will loosely match URLs, numbers, symbols. To avoid always underlining words that
- * we know we will never recognize, this accepts a script identifier that should be one
- * of the SCRIPT_* constants defined above, to rule out quickly characters from very
- * different languages.
- *
- * @param text the string to evaluate.
- * @param script the identifier for the script this spell checker recognizes
- * @return one of the FILTER_OUT_* constants above.
- */
- private static int getCheckabilityInScript(final String text, final int script) {
- if (TextUtils.isEmpty(text) || text.length() <= 1) return CHECKABILITY_TOO_SHORT;
-
- // TODO: check if an equivalent processing can't be done more quickly with a
- // compiled regexp.
- // Filter by first letter
- final int firstCodePoint = text.codePointAt(0);
- // Filter out words that don't start with a letter or an apostrophe
- if (!ScriptUtils.isLetterPartOfScript(firstCodePoint, script)
- && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE;
-
- // Filter contents
- final int length = text.length();
- int letterCount = 0;
- for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
- final int codePoint = text.codePointAt(i);
- // Any word containing a COMMERCIAL_AT is probably an e-mail address
- // Any word containing a SLASH is probably either an ad-hoc combination of two
- // words or a URI - in either case we don't want to spell check that
- if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) {
- return CHECKABILITY_EMAIL_OR_URL;
- }
- // If the string contains a period, native returns strange suggestions (it seems
- // to return suggestions for everything up to the period only and to ignore the
- // rest), so we suppress lookup if there is a period.
- // TODO: investigate why native returns these suggestions and remove this code.
- if (Constants.CODE_PERIOD == codePoint) {
- return CHECKABILITY_CONTAINS_PERIOD;
- }
- if (ScriptUtils.isLetterPartOfScript(codePoint, script)) ++letterCount;
- }
- // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
- // in this word are letters
- return (letterCount * 4 < length * 3)
- ? CHECKABILITY_TOO_MANY_NON_LETTERS : CHECKABILITY_CHECKABLE;
- }
-
- /**
- * Helper method to test valid capitalizations of a word.
- *
- * If the "text" is lower-case, we test only the exact string.
- * If the "Text" is capitalized, we test the exact string "Text" and the lower-cased
- * version of it "text".
- * If the "TEXT" is fully upper case, we test the exact string "TEXT", the lower-cased
- * version of it "text" and the capitalized version of it "Text".
- */
- private boolean isInDictForAnyCapitalization(final String text, final int capitalizeType) {
- // If the word is in there as is, then it's in the dictionary. If not, we'll test lower
- // case versions, but only if the word is not already all-lower case or mixed case.
- if (mService.isValidWord(mLocale, text)) return true;
- if (StringUtils.CAPITALIZE_NONE == capitalizeType) return false;
-
- // If we come here, we have a capitalized word (either First- or All-).
- // Downcase the word and look it up again. If the word is only capitalized, we
- // tested all possibilities, so if it's still negative we can return false.
- final String lowerCaseText = text.toLowerCase(mLocale);
- if (mService.isValidWord(mLocale, lowerCaseText)) return true;
- if (StringUtils.CAPITALIZE_FIRST == capitalizeType) return false;
-
- // If the lower case version is not in the dictionary, it's still possible
- // that we have an all-caps version of a word that needs to be capitalized
- // according to the dictionary. E.g. "GERMANS" only exists in the dictionary as "Germans".
- return mService.isValidWord(mLocale,
- StringUtils.capitalizeFirstAndDowncaseRest(lowerCaseText, mLocale));
- }
-
- // Note : this must be reentrant
- /**
- * Gets a list of suggestions for a specific string. This returns a list of possible
- * corrections for the text passed as an argument. It may split or group words, and
- * even perform grammatical analysis.
- */
- private SuggestionsInfo onGetSuggestionsInternal(final TextInfo textInfo,
- final int suggestionsLimit) {
- return onGetSuggestionsInternal(textInfo, null, suggestionsLimit);
- }
-
- protected SuggestionsInfo onGetSuggestionsInternal(
- final TextInfo textInfo, final NgramContext ngramContext, final int suggestionsLimit) {
- try {
- final String text = textInfo.getText().
- replaceAll(AndroidSpellCheckerService.APOSTROPHE,
- AndroidSpellCheckerService.SINGLE_QUOTE).
- replaceAll("^" + quotesRegexp, "").
- replaceAll(quotesRegexp + "$", "");
-
- if (!mService.hasMainDictionaryForLocale(mLocale)) {
- return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
- false /* reportAsTypo */);
- }
-
- // Handle special patterns like email, URI, telephone number.
- final int checkability = getCheckabilityInScript(text, mScript);
- if (CHECKABILITY_CHECKABLE != checkability) {
- if (CHECKABILITY_CONTAINS_PERIOD == checkability) {
- final String[] splitText = text.split(Constants.REGEXP_PERIOD);
- boolean allWordsAreValid = true;
- for (final String word : splitText) {
- if (!mService.isValidWord(mLocale, word)) {
- allWordsAreValid = false;
- break;
- }
- }
- if (allWordsAreValid) {
- return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
- | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS,
- new String[] {
- TextUtils.join(Constants.STRING_SPACE, splitText) });
- }
- }
- return mService.isValidWord(mLocale, text) ?
- AndroidSpellCheckerService.getInDictEmptySuggestions() :
- AndroidSpellCheckerService.getNotInDictEmptySuggestions(
- CHECKABILITY_CONTAINS_PERIOD == checkability /* reportAsTypo */);
- }
-
- // Handle normal words.
- final int capitalizeType = StringUtils.getCapitalizationType(text);
-
- if (isInDictForAnyCapitalization(text, capitalizeType)) {
- if (DebugFlags.DEBUG_ENABLED) {
- Log.i(TAG, "onGetSuggestionsInternal() : [" + text + "] is a valid word");
- }
- return AndroidSpellCheckerService.getInDictEmptySuggestions();
- }
- if (DebugFlags.DEBUG_ENABLED) {
- Log.i(TAG, "onGetSuggestionsInternal() : [" + text + "] is NOT a valid word");
- }
-
- final Keyboard keyboard = mService.getKeyboardForLocale(mLocale);
- if (null == keyboard) {
- Log.w(TAG, "onGetSuggestionsInternal() : No keyboard for locale: " + mLocale);
- // If there is no keyboard for this locale, don't do any spell-checking.
- return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
- false /* reportAsTypo */);
- }
-
- final WordComposer composer = new WordComposer();
- final int[] codePoints = StringUtils.toCodePointArray(text);
- final int[] coordinates;
- coordinates = keyboard.getCoordinates(codePoints);
- composer.setComposingWord(codePoints, coordinates);
- // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
- final SuggestionResults suggestionResults = mService.getSuggestionResults(
- mLocale, composer.getComposedDataSnapshot(), ngramContext, keyboard);
- final Result result = getResult(capitalizeType, mLocale, suggestionsLimit,
- mService.getRecommendedThreshold(), text, suggestionResults);
- if (DebugFlags.DEBUG_ENABLED) {
- if (result.mSuggestions != null && result.mSuggestions.length > 0) {
- final StringBuilder builder = new StringBuilder();
- for (String suggestion : result.mSuggestions) {
- builder.append(" [");
- builder.append(suggestion);
- builder.append("]");
- }
- Log.i(TAG, "onGetSuggestionsInternal() : Suggestions =" + builder);
- }
- }
- // Handle word not in dictionary.
- // This is called only once per unique word, so entering multiple
- // instances of the same word does not result in more than one call
- // to this method.
- // Also, upon changing the orientation of the device, this is called
- // again for every unique invalid word in the text box.
- StatsUtils.onInvalidWordIdentification(text);
-
- final int flags =
- SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
- | (result.mHasRecommendedSuggestions
- ? SuggestionsInfoCompatUtils
- .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
- : 0);
- final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions);
- mSuggestionsCache.putSuggestionsToCache(text, result.mSuggestions, flags);
- return retval;
- } catch (RuntimeException e) {
- // Don't kill the keyboard if there is a bug in the spell checker
- Log.e(TAG, "Exception while spellchecking", e);
- return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
- false /* reportAsTypo */);
- }
- }
-
- private static final class Result {
- public final String[] mSuggestions;
- public final boolean mHasRecommendedSuggestions;
- public Result(final String[] gatheredSuggestions, final boolean hasRecommendedSuggestions) {
- mSuggestions = gatheredSuggestions;
- mHasRecommendedSuggestions = hasRecommendedSuggestions;
- }
- }
-
- private static Result getResult(final int capitalizeType, final Locale locale,
- final int suggestionsLimit, final float recommendedThreshold, final String originalText,
- final SuggestionResults suggestionResults) {
- if (suggestionResults.isEmpty() || suggestionsLimit <= 0) {
- return new Result(null /* gatheredSuggestions */,
- false /* hasRecommendedSuggestions */);
- }
- final ArrayList<String> suggestions = new ArrayList<>();
- for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) {
- final String suggestion;
- if (StringUtils.CAPITALIZE_ALL == capitalizeType) {
- suggestion = suggestedWordInfo.mWord.toUpperCase(locale);
- } else if (StringUtils.CAPITALIZE_FIRST == capitalizeType) {
- suggestion = StringUtils.capitalizeFirstCodePoint(
- suggestedWordInfo.mWord, locale);
- } else {
- suggestion = suggestedWordInfo.mWord;
- }
- suggestions.add(suggestion);
- }
- StringUtils.removeDupes(suggestions);
- // This returns a String[], while toArray() returns an Object[] which cannot be cast
- // into a String[].
- final List<String> gatheredSuggestionsList =
- suggestions.subList(0, Math.min(suggestions.size(), suggestionsLimit));
- final String[] gatheredSuggestions =
- gatheredSuggestionsList.toArray(new String[gatheredSuggestionsList.size()]);
-
- final int bestScore = suggestionResults.first().mScore;
- final String bestSuggestion = suggestions.get(0);
- final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore(
- originalText, bestSuggestion, bestScore);
- final boolean hasRecommendedSuggestions = (normalizedScore > recommendedThreshold);
- return new Result(gatheredSuggestions, hasRecommendedSuggestions);
- }
-
- /*
- * The spell checker acts on its own behalf. That is needed, in particular, to be able to
- * access the dictionary files, which the provider restricts to the identity of Latin IME.
- * Since it's called externally by the application, the spell checker is using the identity
- * of the application by default unless we clearCallingIdentity.
- * That's what the following method does.
- */
- @Override
- public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, final int suggestionsLimit) {
- long ident = Binder.clearCallingIdentity();
- try {
- return onGetSuggestionsInternal(textInfo, suggestionsLimit);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
deleted file mode 100644
index 10c458c7d..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.annotation.TargetApi;
-import android.content.res.Resources;
-import android.os.Build;
-import android.view.textservice.SentenceSuggestionsInfo;
-import android.view.textservice.SuggestionsInfo;
-import android.view.textservice.TextInfo;
-
-import com.android.inputmethod.compat.TextInfoCompatUtils;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-import com.android.inputmethod.latin.utils.RunInLocale;
-
-import java.util.ArrayList;
-import java.util.Locale;
-
-/**
- * This code is mostly lifted directly from android.service.textservice.SpellCheckerService in
- * the framework; maybe that should be protected instead, so that implementers don't have to
- * rewrite everything for any small change.
- */
-public class SentenceLevelAdapter {
- private static class EmptySentenceSuggestionsInfosInitializationHolder {
- public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS =
- new SentenceSuggestionsInfo[]{};
- }
- private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null);
-
- public static SentenceSuggestionsInfo[] getEmptySentenceSuggestionsInfo() {
- return EmptySentenceSuggestionsInfosInitializationHolder.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
- }
-
- /**
- * Container for split TextInfo parameters
- */
- public static class SentenceWordItem {
- public final TextInfo mTextInfo;
- public final int mStart;
- public final int mLength;
- public SentenceWordItem(TextInfo ti, int start, int end) {
- mTextInfo = ti;
- mStart = start;
- mLength = end - start;
- }
- }
-
- /**
- * Container for originally queried TextInfo and parameters
- */
- public static class SentenceTextInfoParams {
- final TextInfo mOriginalTextInfo;
- final ArrayList<SentenceWordItem> mItems;
- final int mSize;
- public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) {
- mOriginalTextInfo = ti;
- mItems = items;
- mSize = items.size();
- }
- }
-
- private static class WordIterator {
- private final SpacingAndPunctuations mSpacingAndPunctuations;
- public WordIterator(final Resources res, final Locale locale) {
- final RunInLocale<SpacingAndPunctuations> job =
- new RunInLocale<SpacingAndPunctuations>() {
- @Override
- protected SpacingAndPunctuations job(final Resources r) {
- return new SpacingAndPunctuations(r);
- }
- };
- mSpacingAndPunctuations = job.runInLocale(res, locale);
- }
-
- public int getEndOfWord(final CharSequence sequence, final int fromIndex) {
- final int length = sequence.length();
- int index = fromIndex < 0 ? 0 : Character.offsetByCodePoints(sequence, fromIndex, 1);
- while (index < length) {
- final int codePoint = Character.codePointAt(sequence, index);
- if (mSpacingAndPunctuations.isWordSeparator(codePoint)) {
- // If it's a period, we want to stop here only if it's followed by another
- // word separator. In all other cases we stop here.
- if (Constants.CODE_PERIOD == codePoint) {
- final int indexOfNextCodePoint =
- index + Character.charCount(Constants.CODE_PERIOD);
- if (indexOfNextCodePoint < length
- && mSpacingAndPunctuations.isWordSeparator(
- Character.codePointAt(sequence, indexOfNextCodePoint))) {
- return index;
- }
- } else {
- return index;
- }
- }
- index += Character.charCount(codePoint);
- }
- return index;
- }
-
- public int getBeginningOfNextWord(final CharSequence sequence, final int fromIndex) {
- final int length = sequence.length();
- if (fromIndex >= length) {
- return -1;
- }
- int index = fromIndex < 0 ? 0 : Character.offsetByCodePoints(sequence, fromIndex, 1);
- while (index < length) {
- final int codePoint = Character.codePointAt(sequence, index);
- if (!mSpacingAndPunctuations.isWordSeparator(codePoint)) {
- return index;
- }
- index += Character.charCount(codePoint);
- }
- return -1;
- }
- }
-
- private final WordIterator mWordIterator;
- public SentenceLevelAdapter(final Resources res, final Locale locale) {
- mWordIterator = new WordIterator(res, locale);
- }
-
- public SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) {
- final WordIterator wordIterator = mWordIterator;
- final CharSequence originalText =
- TextInfoCompatUtils.getCharSequenceOrString(originalTextInfo);
- final int cookie = originalTextInfo.getCookie();
- final int start = -1;
- final int end = originalText.length();
- final ArrayList<SentenceWordItem> wordItems = new ArrayList<>();
- int wordStart = wordIterator.getBeginningOfNextWord(originalText, start);
- int wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
- while (wordStart <= end && wordEnd != -1 && wordStart != -1) {
- if (wordEnd >= start && wordEnd > wordStart) {
- final TextInfo ti = TextInfoCompatUtils.newInstance(originalText, wordStart,
- wordEnd, cookie, originalText.subSequence(wordStart, wordEnd).hashCode());
- wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
- }
- wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd);
- if (wordStart == -1) {
- break;
- }
- wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
- }
- return new SentenceTextInfoParams(originalTextInfo, wordItems);
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- public static SentenceSuggestionsInfo reconstructSuggestions(
- SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) {
- if (results == null || results.length == 0) {
- return null;
- }
- if (originalTextInfoParams == null) {
- return null;
- }
- final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie();
- final int originalSequence =
- originalTextInfoParams.mOriginalTextInfo.getSequence();
-
- final int querySize = originalTextInfoParams.mSize;
- final int[] offsets = new int[querySize];
- final int[] lengths = new int[querySize];
- final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize];
- for (int i = 0; i < querySize; ++i) {
- final SentenceWordItem item = originalTextInfoParams.mItems.get(i);
- SuggestionsInfo result = null;
- for (int j = 0; j < results.length; ++j) {
- final SuggestionsInfo cur = results[j];
- if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) {
- result = cur;
- result.setCookieAndSequence(originalCookie, originalSequence);
- break;
- }
- }
- offsets[i] = item.mStart;
- lengths[i] = item.mLength;
- reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO;
- }
- return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
deleted file mode 100644
index 5f99f9004..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import com.android.inputmethod.latin.permissions.PermissionsManager;
-import com.android.inputmethod.latin.utils.FragmentUtils;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import androidx.core.app.ActivityCompat;
-
-/**
- * Spell checker preference screen.
- */
-public final class SpellCheckerSettingsActivity extends PreferenceActivity
- implements ActivityCompat.OnRequestPermissionsResultCallback {
- private static final String DEFAULT_FRAGMENT = SpellCheckerSettingsFragment.class.getName();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public Intent getIntent() {
- final Intent modIntent = new Intent(super.getIntent());
- modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT);
- modIntent.putExtra(EXTRA_NO_HEADERS, true);
- return modIntent;
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- @Override
- public boolean isValidFragment(String fragmentName) {
- return FragmentUtils.isValidFragment(fragmentName);
- }
-
- @Override
- public void onRequestPermissionsResult(
- int requestCode, String[] permissions, int[] grantResults) {
- PermissionsManager.get(this).onRequestPermissionsResult(
- requestCode, permissions, grantResults);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
deleted file mode 100644
index 12005c25e..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.spellcheck;
-
-import android.Manifest;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.preference.PreferenceScreen;
-import android.preference.SwitchPreference;
-import android.text.TextUtils;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.permissions.PermissionsManager;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.settings.SubScreenFragment;
-import com.android.inputmethod.latin.settings.TwoStatePreferenceHelper;
-import com.android.inputmethod.latin.utils.ApplicationUtils;
-
-import static com.android.inputmethod.latin.permissions.PermissionsManager.get;
-
-/**
- * Preference screen.
- */
-public final class SpellCheckerSettingsFragment extends SubScreenFragment
- implements SharedPreferences.OnSharedPreferenceChangeListener,
- PermissionsManager.PermissionsResultCallback {
-
- private SwitchPreference mLookupContactsPreference;
-
- @Override
- public void onActivityCreated(final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- addPreferencesFromResource(R.xml.spell_checker_settings);
- final PreferenceScreen preferenceScreen = getPreferenceScreen();
- preferenceScreen.setTitle(ApplicationUtils.getActivityTitleResId(
- getActivity(), SpellCheckerSettingsActivity.class));
- TwoStatePreferenceHelper.replaceCheckBoxPreferencesBySwitchPreferences(preferenceScreen);
-
- mLookupContactsPreference = (SwitchPreference) findPreference(
- AndroidSpellCheckerService.PREF_USE_CONTACTS_KEY);
- turnOffLookupContactsIfNoPermission();
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (!TextUtils.equals(key, AndroidSpellCheckerService.PREF_USE_CONTACTS_KEY)) {
- return;
- }
-
- if (!sharedPreferences.getBoolean(key, false)) {
- // don't care if the preference is turned off.
- return;
- }
-
- // Check for permissions.
- if (PermissionsUtil.checkAllPermissionsGranted(
- getActivity() /* context */, Manifest.permission.READ_CONTACTS)) {
- return; // all permissions granted, no need to request permissions.
- }
-
- get(getActivity() /* context */).requestPermissions(this /* PermissionsResultCallback */,
- getActivity() /* activity */, Manifest.permission.READ_CONTACTS);
- }
-
- @Override
- public void onRequestPermissionsResult(boolean allGranted) {
- turnOffLookupContactsIfNoPermission();
- }
-
- private void turnOffLookupContactsIfNoPermission() {
- if (!PermissionsUtil.checkAllPermissionsGranted(
- getActivity(), Manifest.permission.READ_CONTACTS)) {
- mLookupContactsPreference.setChecked(false);
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
deleted file mode 100644
index 37ab2669b..000000000
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.suggestions;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.TypefaceUtils;
-
-public final class MoreSuggestions extends Keyboard {
- public final SuggestedWords mSuggestedWords;
-
- MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) {
- super(params);
- mSuggestedWords = suggestedWords;
- }
-
- private static final class MoreSuggestionsParam extends KeyboardParams {
- private final int[] mWidths = new int[SuggestedWords.MAX_SUGGESTIONS];
- private final int[] mRowNumbers = new int[SuggestedWords.MAX_SUGGESTIONS];
- private final int[] mColumnOrders = new int[SuggestedWords.MAX_SUGGESTIONS];
- private final int[] mNumColumnsInRow = new int[SuggestedWords.MAX_SUGGESTIONS];
- private static final int MAX_COLUMNS_IN_ROW = 3;
- private int mNumRows;
- public Drawable mDivider;
- public int mDividerWidth;
-
- public MoreSuggestionsParam() {
- super();
- }
-
- public int layout(final SuggestedWords suggestedWords, final int fromIndex,
- final int maxWidth, final int minWidth, final int maxRow, final Paint paint,
- final Resources res) {
- clearKeys();
- mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
- mDividerWidth = mDivider.getIntrinsicWidth();
- final float padding = res.getDimension(
- R.dimen.config_more_suggestions_key_horizontal_padding);
-
- int row = 0;
- int index = fromIndex;
- int rowStartIndex = fromIndex;
- final int size = Math.min(suggestedWords.size(), SuggestedWords.MAX_SUGGESTIONS);
- while (index < size) {
- final String word;
- if (isIndexSubjectToAutoCorrection(suggestedWords, index)) {
- // INDEX_OF_AUTO_CORRECTION and INDEX_OF_TYPED_WORD got swapped.
- word = suggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD);
- } else {
- word = suggestedWords.getLabel(index);
- }
- // TODO: Should take care of text x-scaling.
- mWidths[index] = (int)(TypefaceUtils.getStringWidth(word, paint) + padding);
- final int numColumn = index - rowStartIndex + 1;
- final int columnWidth =
- (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
- if (numColumn > MAX_COLUMNS_IN_ROW
- || !fitInWidth(rowStartIndex, index + 1, columnWidth)) {
- if ((row + 1) >= maxRow) {
- break;
- }
- mNumColumnsInRow[row] = index - rowStartIndex;
- rowStartIndex = index;
- row++;
- }
- mColumnOrders[index] = index - rowStartIndex;
- mRowNumbers[index] = row;
- index++;
- }
- mNumColumnsInRow[row] = index - rowStartIndex;
- mNumRows = row + 1;
- mBaseWidth = mOccupiedWidth = Math.max(
- minWidth, calcurateMaxRowWidth(fromIndex, index));
- mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
- return index - fromIndex;
- }
-
- private boolean fitInWidth(final int startIndex, final int endIndex, final int width) {
- for (int index = startIndex; index < endIndex; index++) {
- if (mWidths[index] > width)
- return false;
- }
- return true;
- }
-
- private int calcurateMaxRowWidth(final int startIndex, final int endIndex) {
- int maxRowWidth = 0;
- int index = startIndex;
- for (int row = 0; row < mNumRows; row++) {
- final int numColumnInRow = mNumColumnsInRow[row];
- int maxKeyWidth = 0;
- while (index < endIndex && mRowNumbers[index] == row) {
- maxKeyWidth = Math.max(maxKeyWidth, mWidths[index]);
- index++;
- }
- maxRowWidth = Math.max(maxRowWidth,
- maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1));
- }
- return maxRowWidth;
- }
-
- private static final int[][] COLUMN_ORDER_TO_NUMBER = {
- { 0 }, // center
- { 1, 0 }, // right-left
- { 1, 0, 2 }, // center-left-right
- };
-
- public int getNumColumnInRow(final int index) {
- return mNumColumnsInRow[mRowNumbers[index]];
- }
-
- public int getColumnNumber(final int index) {
- final int columnOrder = mColumnOrders[index];
- final int numColumn = getNumColumnInRow(index);
- return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
- }
-
- public int getX(final int index) {
- final int columnNumber = getColumnNumber(index);
- return columnNumber * (getWidth(index) + mDividerWidth);
- }
-
- public int getY(final int index) {
- final int row = mRowNumbers[index];
- return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
- }
-
- public int getWidth(final int index) {
- final int numColumnInRow = getNumColumnInRow(index);
- return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
- }
-
- public void markAsEdgeKey(final Key key, final int index) {
- final int row = mRowNumbers[index];
- if (row == 0)
- key.markAsBottomEdge(this);
- if (row == mNumRows - 1)
- key.markAsTopEdge(this);
-
- final int numColumnInRow = mNumColumnsInRow[row];
- final int column = getColumnNumber(index);
- if (column == 0)
- key.markAsLeftEdge(this);
- if (column == numColumnInRow - 1)
- key.markAsRightEdge(this);
- }
- }
-
- static boolean isIndexSubjectToAutoCorrection(final SuggestedWords suggestedWords,
- final int index) {
- return suggestedWords.mWillAutoCorrect && index == SuggestedWords.INDEX_OF_AUTO_CORRECTION;
- }
-
- public static final class Builder extends KeyboardBuilder<MoreSuggestionsParam> {
- private final MoreSuggestionsView mPaneView;
- private SuggestedWords mSuggestedWords;
- private int mFromIndex;
- private int mToIndex;
-
- public Builder(final Context context, final MoreSuggestionsView paneView) {
- super(context, new MoreSuggestionsParam());
- mPaneView = paneView;
- }
-
- public Builder layout(final SuggestedWords suggestedWords, final int fromIndex,
- final int maxWidth, final int minWidth, final int maxRow,
- final Keyboard parentKeyboard) {
- final int xmlId = R.xml.kbd_suggestions_pane_template;
- load(xmlId, parentKeyboard.mId);
- mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
- mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight);
- final int count = mParams.layout(suggestedWords, fromIndex, maxWidth, minWidth, maxRow,
- mPaneView.newLabelPaint(null /* key */), mResources);
- mFromIndex = fromIndex;
- mToIndex = fromIndex + count;
- mSuggestedWords = suggestedWords;
- return this;
- }
-
- @Override
- public MoreSuggestions build() {
- final MoreSuggestionsParam params = mParams;
- for (int index = mFromIndex; index < mToIndex; index++) {
- final int x = params.getX(index);
- final int y = params.getY(index);
- final int width = params.getWidth(index);
- final String word;
- final String info;
- if (isIndexSubjectToAutoCorrection(mSuggestedWords, index)) {
- // INDEX_OF_AUTO_CORRECTION and INDEX_OF_TYPED_WORD got swapped.
- word = mSuggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD);
- info = mSuggestedWords.getDebugString(SuggestedWords.INDEX_OF_TYPED_WORD);
- } else {
- word = mSuggestedWords.getLabel(index);
- info = mSuggestedWords.getDebugString(index);
- }
- final Key key = new MoreSuggestionKey(word, info, index, params);
- params.markAsEdgeKey(key, index);
- params.onAddKey(key);
- final int columnNumber = params.getColumnNumber(index);
- final int numColumnInRow = params.getNumColumnInRow(index);
- if (columnNumber < numColumnInRow - 1) {
- final Divider divider = new Divider(params, params.mDivider, x + width, y,
- params.mDividerWidth, params.mDefaultRowHeight);
- params.onAddKey(divider);
- }
- }
- return new MoreSuggestions(params, mSuggestedWords);
- }
- }
-
- static final class MoreSuggestionKey extends Key {
- public final int mSuggestedWordIndex;
-
- public MoreSuggestionKey(final String word, final String info, final int index,
- final MoreSuggestionsParam params) {
- super(word /* label */, KeyboardIconsSet.ICON_UNDEFINED, Constants.CODE_OUTPUT_TEXT,
- word /* outputText */, info, 0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL,
- params.getX(index), params.getY(index), params.getWidth(index),
- params.mDefaultRowHeight, params.mHorizontalGap, params.mVerticalGap);
- mSuggestedWordIndex = index;
- }
- }
-
- private static final class Divider extends Key.Spacer {
- private final Drawable mIcon;
-
- public Divider(final KeyboardParams params, final Drawable icon, final int x,
- final int y, final int width, final int height) {
- super(params, x, y, width, height);
- mIcon = icon;
- }
-
- @Override
- public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
- // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
- // constructor.
- // TODO: Drawable itself should have an alpha value.
- mIcon.setAlpha(128);
- return mIcon;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
deleted file mode 100644
index 907e3fa42..000000000
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.suggestions;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
-import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionKey;
-
-/**
- * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
- * key presses and touch movements.
- */
-public final class MoreSuggestionsView extends MoreKeysKeyboardView {
- private static final String TAG = MoreSuggestionsView.class.getSimpleName();
-
- public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
- public abstract void onSuggestionSelected(final SuggestedWordInfo info);
- }
-
- private boolean mIsInModalMode;
-
- public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
- }
-
- public MoreSuggestionsView(final Context context, final AttributeSet attrs,
- final int defStyle) {
- super(context, attrs, defStyle);
- }
-
- // TODO: Remove redundant override method.
- @Override
- public void setKeyboard(final Keyboard keyboard) {
- super.setKeyboard(keyboard);
- mIsInModalMode = false;
- // With accessibility mode off, {@link #mAccessibilityDelegate} is set to null at the
- // above {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call.
- // With accessibility mode on, {@link #mAccessibilityDelegate} is set to a
- // {@link MoreKeysKeyboardAccessibilityDelegate} object at the above
- // {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call.
- if (mAccessibilityDelegate != null) {
- mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_suggestions);
- mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_suggestions);
- }
- }
-
- @Override
- protected int getDefaultCoordX() {
- final MoreSuggestions pane = (MoreSuggestions)getKeyboard();
- return pane.mOccupiedWidth / 2;
- }
-
- public void updateKeyboardGeometry(final int keyHeight) {
- updateKeyDrawParams(keyHeight);
- }
-
- public void setModalMode() {
- mIsInModalMode = true;
- // Set vertical correction to zero (Reset more keys keyboard sliding allowance
- // {@link R#dimen.config_more_keys_keyboard_slide_allowance}).
- mKeyDetector.setKeyboard(getKeyboard(), -getPaddingLeft(), -getPaddingTop());
- }
-
- public boolean isInModalMode() {
- return mIsInModalMode;
- }
-
- @Override
- protected void onKeyInput(final Key key, final int x, final int y) {
- if (!(key instanceof MoreSuggestionKey)) {
- Log.e(TAG, "Expected key is MoreSuggestionKey, but found "
- + key.getClass().getName());
- return;
- }
- final Keyboard keyboard = getKeyboard();
- if (!(keyboard instanceof MoreSuggestions)) {
- Log.e(TAG, "Expected keyboard is MoreSuggestions, but found "
- + keyboard.getClass().getName());
- return;
- }
- final SuggestedWords suggestedWords = ((MoreSuggestions)keyboard).mSuggestedWords;
- final int index = ((MoreSuggestionKey)key).mSuggestedWordIndex;
- if (index < 0 || index >= suggestedWords.size()) {
- Log.e(TAG, "Selected suggestion has an illegal index: " + index);
- return;
- }
- if (!(mListener instanceof MoreSuggestionsListener)) {
- Log.e(TAG, "Expected mListener is MoreSuggestionsListener, but found "
- + mListener.getClass().getName());
- return;
- }
- ((MoreSuggestionsListener)mListener).onSuggestionSelected(suggestedWords.getInfo(index));
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
deleted file mode 100644
index 9577d0913..000000000
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.suggestions;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.style.CharacterStyle;
-import android.text.style.StyleSpan;
-import android.text.style.UnderlineSpan;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.PunctuationSuggestions;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.ViewLayoutUtils;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-final class SuggestionStripLayoutHelper {
- private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
- private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
- private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
- private static final int PUNCTUATIONS_IN_STRIP = 5;
- private static final float MIN_TEXT_XSCALE = 0.70f;
-
- public final int mPadding;
- public final int mDividerWidth;
- public final int mSuggestionsStripHeight;
- private final int mSuggestionsCountInStrip;
- public final int mMoreSuggestionsRowHeight;
- private int mMaxMoreSuggestionsRow;
- public final float mMinMoreSuggestionsWidth;
- public final int mMoreSuggestionsBottomGap;
- private boolean mMoreSuggestionsAvailable;
-
- // The index of these {@link ArrayList} is the position in the suggestion strip. The indices
- // increase towards the right for LTR scripts and the left for RTL scripts, starting with 0.
- // The position of the most important suggestion is in {@link #mCenterPositionInStrip}
- private final ArrayList<TextView> mWordViews;
- private final ArrayList<View> mDividerViews;
- private final ArrayList<TextView> mDebugInfoViews;
-
- private final int mColorValidTypedWord;
- private final int mColorTypedWord;
- private final int mColorAutoCorrect;
- private final int mColorSuggested;
- private final float mAlphaObsoleted;
- private final float mCenterSuggestionWeight;
- private final int mCenterPositionInStrip;
- private final int mTypedWordPositionWhenAutocorrect;
- private final Drawable mMoreSuggestionsHint;
- private static final String MORE_SUGGESTIONS_HINT = "\u2026";
-
- private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
- private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
-
- private final int mSuggestionStripOptions;
- // These constants are the flag values of
- // {@link R.styleable#SuggestionStripView_suggestionStripOptions} attribute.
- private static final int AUTO_CORRECT_BOLD = 0x01;
- private static final int AUTO_CORRECT_UNDERLINE = 0x02;
- private static final int VALID_TYPED_WORD_BOLD = 0x04;
-
- public SuggestionStripLayoutHelper(final Context context, final AttributeSet attrs,
- final int defStyle, final ArrayList<TextView> wordViews,
- final ArrayList<View> dividerViews, final ArrayList<TextView> debugInfoViews) {
- mWordViews = wordViews;
- mDividerViews = dividerViews;
- mDebugInfoViews = debugInfoViews;
-
- final TextView wordView = wordViews.get(0);
- final View dividerView = dividerViews.get(0);
- mPadding = wordView.getCompoundPaddingLeft() + wordView.getCompoundPaddingRight();
- dividerView.measure(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- mDividerWidth = dividerView.getMeasuredWidth();
-
- final Resources res = wordView.getResources();
- mSuggestionsStripHeight = res.getDimensionPixelSize(
- R.dimen.config_suggestions_strip_height);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripView);
- mSuggestionStripOptions = a.getInt(
- R.styleable.SuggestionStripView_suggestionStripOptions, 0);
- mAlphaObsoleted = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_alphaObsoleted, 1.0f);
- mColorValidTypedWord = a.getColor(R.styleable.SuggestionStripView_colorValidTypedWord, 0);
- mColorTypedWord = a.getColor(R.styleable.SuggestionStripView_colorTypedWord, 0);
- mColorAutoCorrect = a.getColor(R.styleable.SuggestionStripView_colorAutoCorrect, 0);
- mColorSuggested = a.getColor(R.styleable.SuggestionStripView_colorSuggested, 0);
- mSuggestionsCountInStrip = a.getInt(
- R.styleable.SuggestionStripView_suggestionsCountInStrip,
- DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
- mCenterSuggestionWeight = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_centerSuggestionPercentile,
- DEFAULT_CENTER_SUGGESTION_PERCENTILE);
- mMaxMoreSuggestionsRow = a.getInt(
- R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
- DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
- mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f);
- a.recycle();
-
- mMoreSuggestionsHint = getMoreSuggestionsHint(res,
- res.getDimension(R.dimen.config_more_suggestions_hint_text_size),
- mColorAutoCorrect);
- mCenterPositionInStrip = mSuggestionsCountInStrip / 2;
- // Assuming there are at least three suggestions. Also, note that the suggestions are
- // laid out according to script direction, so this is left of the center for LTR scripts
- // and right of the center for RTL scripts.
- mTypedWordPositionWhenAutocorrect = mCenterPositionInStrip - 1;
- mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
- R.dimen.config_more_suggestions_bottom_gap);
- mMoreSuggestionsRowHeight = res.getDimensionPixelSize(
- R.dimen.config_more_suggestions_row_height);
- }
-
- public int getMaxMoreSuggestionsRow() {
- return mMaxMoreSuggestionsRow;
- }
-
- private int getMoreSuggestionsHeight() {
- return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
- }
-
- public void setMoreSuggestionsHeight(final int remainingHeight) {
- final int currentHeight = getMoreSuggestionsHeight();
- if (currentHeight <= remainingHeight) {
- return;
- }
-
- mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap)
- / mMoreSuggestionsRowHeight;
- }
-
- private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
- final int color) {
- final Paint paint = new Paint();
- paint.setAntiAlias(true);
- paint.setTextAlign(Align.CENTER);
- paint.setTextSize(textSize);
- paint.setColor(color);
- final Rect bounds = new Rect();
- paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds);
- final int width = Math.round(bounds.width() + 0.5f);
- final int height = Math.round(bounds.height() + 0.5f);
- final Bitmap buffer = Bitmap.createBitmap(width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(buffer);
- canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
- BitmapDrawable bitmapDrawable = new BitmapDrawable(res, buffer);
- bitmapDrawable.setTargetDensity(canvas);
- return bitmapDrawable;
- }
-
- private CharSequence getStyledSuggestedWord(final SuggestedWords suggestedWords,
- final int indexInSuggestedWords) {
- if (indexInSuggestedWords >= suggestedWords.size()) {
- return null;
- }
- final String word = suggestedWords.getLabel(indexInSuggestedWords);
- // TODO: don't use the index to decide whether this is the auto-correction/typed word, as
- // this is brittle
- final boolean isAutoCorrection = suggestedWords.mWillAutoCorrect
- && indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION;
- final boolean isTypedWordValid = suggestedWords.mTypedWordValid
- && indexInSuggestedWords == SuggestedWords.INDEX_OF_TYPED_WORD;
- if (!isAutoCorrection && !isTypedWordValid) {
- return word;
- }
-
- final Spannable spannedWord = new SpannableString(word);
- final int options = mSuggestionStripOptions;
- if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
- || (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
- addStyleSpan(spannedWord, BOLD_SPAN);
- }
- if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
- addStyleSpan(spannedWord, UNDERLINE_SPAN);
- }
- return spannedWord;
- }
-
- /**
- * Convert an index of {@link SuggestedWords} to position in the suggestion strip.
- * @param indexInSuggestedWords the index of {@link SuggestedWords}.
- * @param suggestedWords the suggested words list
- * @return Non-negative integer of the position in the suggestion strip.
- * Negative integer if the word of the index shouldn't be shown on the suggestion strip.
- */
- private int getPositionInSuggestionStrip(final int indexInSuggestedWords,
- final SuggestedWords suggestedWords) {
- final SettingsValues settingsValues = Settings.getInstance().getCurrent();
- final boolean shouldOmitTypedWord = shouldOmitTypedWord(suggestedWords.mInputStyle,
- settingsValues.mGestureFloatingPreviewTextEnabled,
- settingsValues.mShouldShowLxxSuggestionUi);
- return getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords.mWillAutoCorrect,
- settingsValues.mShouldShowLxxSuggestionUi && shouldOmitTypedWord,
- mCenterPositionInStrip, mTypedWordPositionWhenAutocorrect);
- }
-
- @UsedForTesting
- static boolean shouldOmitTypedWord(final int inputStyle,
- final boolean gestureFloatingPreviewTextEnabled,
- final boolean shouldShowUiToAcceptTypedWord) {
- final boolean omitTypedWord = (inputStyle == SuggestedWords.INPUT_STYLE_TYPING)
- || (inputStyle == SuggestedWords.INPUT_STYLE_TAIL_BATCH)
- || (inputStyle == SuggestedWords.INPUT_STYLE_UPDATE_BATCH
- && gestureFloatingPreviewTextEnabled);
- return shouldShowUiToAcceptTypedWord && omitTypedWord;
- }
-
- @UsedForTesting
- static int getPositionInSuggestionStrip(final int indexInSuggestedWords,
- final boolean willAutoCorrect, final boolean omitTypedWord,
- final int centerPositionInStrip, final int typedWordPositionWhenAutoCorrect) {
- if (omitTypedWord) {
- if (indexInSuggestedWords == SuggestedWords.INDEX_OF_TYPED_WORD) {
- // Ignore.
- return -1;
- }
- if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION) {
- // Center in the suggestion strip.
- return centerPositionInStrip;
- }
- // If neither of those, the order in the suggestion strip is left of the center first
- // then right of the center, to both edges of the suggestion strip.
- // For example, center-1, center+1, center-2, center+2, and so on.
- final int n = indexInSuggestedWords;
- final int offsetFromCenter = (n % 2) == 0 ? -(n / 2) : (n / 2);
- final int positionInSuggestionStrip = centerPositionInStrip + offsetFromCenter;
- return positionInSuggestionStrip;
- }
- final int indexToDisplayMostImportantSuggestion;
- final int indexToDisplaySecondMostImportantSuggestion;
- if (willAutoCorrect) {
- indexToDisplayMostImportantSuggestion = SuggestedWords.INDEX_OF_AUTO_CORRECTION;
- indexToDisplaySecondMostImportantSuggestion = SuggestedWords.INDEX_OF_TYPED_WORD;
- } else {
- indexToDisplayMostImportantSuggestion = SuggestedWords.INDEX_OF_TYPED_WORD;
- indexToDisplaySecondMostImportantSuggestion = SuggestedWords.INDEX_OF_AUTO_CORRECTION;
- }
- if (indexInSuggestedWords == indexToDisplayMostImportantSuggestion) {
- // Center in the suggestion strip.
- return centerPositionInStrip;
- }
- if (indexInSuggestedWords == indexToDisplaySecondMostImportantSuggestion) {
- // Center-1.
- return typedWordPositionWhenAutoCorrect;
- }
- // If neither of those, the order in the suggestion strip is right of the center first
- // then left of the center, to both edges of the suggestion strip.
- // For example, Center+1, center-2, center+2, center-3, and so on.
- final int n = indexInSuggestedWords + 1;
- final int offsetFromCenter = (n % 2) == 0 ? -(n / 2) : (n / 2);
- final int positionInSuggestionStrip = centerPositionInStrip + offsetFromCenter;
- return positionInSuggestionStrip;
- }
-
- private int getSuggestionTextColor(final SuggestedWords suggestedWords,
- final int indexInSuggestedWords) {
- // Use identity for strings, not #equals : it's the typed word if it's the same object
- final boolean isTypedWord = suggestedWords.getInfo(indexInSuggestedWords).isKindOf(
- SuggestedWordInfo.KIND_TYPED);
-
- final int color;
- if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION
- && suggestedWords.mWillAutoCorrect) {
- color = mColorAutoCorrect;
- } else if (isTypedWord && suggestedWords.mTypedWordValid) {
- color = mColorValidTypedWord;
- } else if (isTypedWord) {
- color = mColorTypedWord;
- } else {
- color = mColorSuggested;
- }
- if (suggestedWords.mIsObsoleteSuggestions && !isTypedWord) {
- return applyAlpha(color, mAlphaObsoleted);
- }
- return color;
- }
-
- private static int applyAlpha(final int color, final float alpha) {
- final int newAlpha = (int)(Color.alpha(color) * alpha);
- return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
- }
-
- private static void addDivider(final ViewGroup stripView, final View dividerView) {
- stripView.addView(dividerView);
- final LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams)dividerView.getLayoutParams();
- params.gravity = Gravity.CENTER;
- }
-
- /**
- * Layout suggestions to the suggestions strip. And returns the start index of more
- * suggestions.
- *
- * @param suggestedWords suggestions to be shown in the suggestions strip.
- * @param stripView the suggestions strip view.
- * @param placerView the view where the debug info will be placed.
- * @return the start index of more suggestions.
- */
- public int layoutAndReturnStartIndexOfMoreSuggestions(
- final Context context,
- final SuggestedWords suggestedWords,
- final ViewGroup stripView,
- final ViewGroup placerView) {
- if (suggestedWords.isPunctuationSuggestions()) {
- return layoutPunctuationsAndReturnStartIndexOfMoreSuggestions(
- (PunctuationSuggestions)suggestedWords, stripView);
- }
-
- final int wordCountToShow = suggestedWords.getWordCountToShow(
- Settings.getInstance().getCurrent().mShouldShowLxxSuggestionUi);
- final int startIndexOfMoreSuggestions = setupWordViewsAndReturnStartIndexOfMoreSuggestions(
- suggestedWords, mSuggestionsCountInStrip);
- final TextView centerWordView = mWordViews.get(mCenterPositionInStrip);
- final int stripWidth = stripView.getWidth();
- final int centerWidth = getSuggestionWidth(mCenterPositionInStrip, stripWidth);
- if (wordCountToShow == 1 || getTextScaleX(centerWordView.getText(), centerWidth,
- centerWordView.getPaint()) < MIN_TEXT_XSCALE) {
- // Layout only the most relevant suggested word at the center of the suggestion strip
- // by consolidating all slots in the strip.
- final int countInStrip = 1;
- mMoreSuggestionsAvailable = (wordCountToShow > countInStrip);
- layoutWord(context, mCenterPositionInStrip, stripWidth - mPadding);
- stripView.addView(centerWordView);
- setLayoutWeight(centerWordView, 1.0f, ViewGroup.LayoutParams.MATCH_PARENT);
- if (SuggestionStripView.DBG) {
- layoutDebugInfo(mCenterPositionInStrip, placerView, stripWidth);
- }
- final Integer lastIndex = (Integer)centerWordView.getTag();
- return (lastIndex == null ? 0 : lastIndex) + 1;
- }
-
- final int countInStrip = mSuggestionsCountInStrip;
- mMoreSuggestionsAvailable = (wordCountToShow > countInStrip);
- @SuppressWarnings("unused")
- int x = 0;
- for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) {
- if (positionInStrip != 0) {
- final View divider = mDividerViews.get(positionInStrip);
- // Add divider if this isn't the left most suggestion in suggestions strip.
- addDivider(stripView, divider);
- x += divider.getMeasuredWidth();
- }
-
- final int width = getSuggestionWidth(positionInStrip, stripWidth);
- final TextView wordView = layoutWord(context, positionInStrip, width);
- stripView.addView(wordView);
- setLayoutWeight(wordView, getSuggestionWeight(positionInStrip),
- ViewGroup.LayoutParams.MATCH_PARENT);
- x += wordView.getMeasuredWidth();
-
- if (SuggestionStripView.DBG) {
- layoutDebugInfo(positionInStrip, placerView, x);
- }
- }
- return startIndexOfMoreSuggestions;
- }
-
- /**
- * Format appropriately the suggested word in {@link #mWordViews} specified by
- * <code>positionInStrip</code>. When the suggested word doesn't exist, the corresponding
- * {@link TextView} will be disabled and never respond to user interaction. The suggested word
- * may be shrunk or ellipsized to fit in the specified width.
- *
- * The <code>positionInStrip</code> argument is the index in the suggestion strip. The indices
- * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0.
- * The position of the most important suggestion is in {@link #mCenterPositionInStrip}. This
- * usually doesn't match the index in <code>suggedtedWords</code> -- see
- * {@link #getPositionInSuggestionStrip(int,SuggestedWords)}.
- *
- * @param positionInStrip the position in the suggestion strip.
- * @param width the maximum width for layout in pixels.
- * @return the {@link TextView} containing the suggested word appropriately formatted.
- */
- private TextView layoutWord(final Context context, final int positionInStrip, final int width) {
- final TextView wordView = mWordViews.get(positionInStrip);
- final CharSequence word = wordView.getText();
- if (positionInStrip == mCenterPositionInStrip && mMoreSuggestionsAvailable) {
- // TODO: This "more suggestions hint" should have a nicely designed icon.
- wordView.setCompoundDrawablesWithIntrinsicBounds(
- null, null, null, mMoreSuggestionsHint);
- // HACK: Align with other TextViews that have no compound drawables.
- wordView.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
- } else {
- wordView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
- }
- // {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
- // Use a simple {@link String} to avoid the issue.
- wordView.setContentDescription(
- TextUtils.isEmpty(word)
- ? context.getResources().getString(R.string.spoken_empty_suggestion)
- : word.toString());
- final CharSequence text = getEllipsizedTextWithSettingScaleX(
- word, width, wordView.getPaint());
- final float scaleX = wordView.getTextScaleX();
- wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
- wordView.setTextScaleX(scaleX);
- // A <code>wordView</code> should be disabled when <code>word</code> is empty in order to
- // make it unclickable.
- // With accessibility touch exploration on, <code>wordView</code> should be enabled even
- // when it is empty to avoid announcing as "disabled".
- wordView.setEnabled(!TextUtils.isEmpty(word)
- || AccessibilityUtils.getInstance().isTouchExplorationEnabled());
- return wordView;
- }
-
- private void layoutDebugInfo(final int positionInStrip, final ViewGroup placerView,
- final int x) {
- final TextView debugInfoView = mDebugInfoViews.get(positionInStrip);
- final CharSequence debugInfo = debugInfoView.getText();
- if (debugInfo == null) {
- return;
- }
- placerView.addView(debugInfoView);
- debugInfoView.measure(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- final int infoWidth = debugInfoView.getMeasuredWidth();
- final int y = debugInfoView.getMeasuredHeight();
- ViewLayoutUtils.placeViewAt(
- debugInfoView, x - infoWidth, y, infoWidth, debugInfoView.getMeasuredHeight());
- }
-
- private int getSuggestionWidth(final int positionInStrip, final int maxWidth) {
- final int paddings = mPadding * mSuggestionsCountInStrip;
- final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
- final int availableWidth = maxWidth - paddings - dividers;
- return (int)(availableWidth * getSuggestionWeight(positionInStrip));
- }
-
- private float getSuggestionWeight(final int positionInStrip) {
- if (positionInStrip == mCenterPositionInStrip) {
- return mCenterSuggestionWeight;
- }
- // TODO: Revisit this for cases of 5 or more suggestions
- return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1);
- }
-
- private int setupWordViewsAndReturnStartIndexOfMoreSuggestions(
- final SuggestedWords suggestedWords, final int maxSuggestionInStrip) {
- // Clear all suggestions first
- for (int positionInStrip = 0; positionInStrip < maxSuggestionInStrip; ++positionInStrip) {
- final TextView wordView = mWordViews.get(positionInStrip);
- wordView.setText(null);
- wordView.setTag(null);
- // Make this inactive for touches in {@link #layoutWord(int,int)}.
- if (SuggestionStripView.DBG) {
- mDebugInfoViews.get(positionInStrip).setText(null);
- }
- }
- int count = 0;
- int indexInSuggestedWords;
- for (indexInSuggestedWords = 0; indexInSuggestedWords < suggestedWords.size()
- && count < maxSuggestionInStrip; indexInSuggestedWords++) {
- final int positionInStrip =
- getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords);
- if (positionInStrip < 0) {
- continue;
- }
- final TextView wordView = mWordViews.get(positionInStrip);
- // {@link TextView#getTag()} is used to get the index in suggestedWords at
- // {@link SuggestionStripView#onClick(View)}.
- wordView.setTag(indexInSuggestedWords);
- wordView.setText(getStyledSuggestedWord(suggestedWords, indexInSuggestedWords));
- wordView.setTextColor(getSuggestionTextColor(suggestedWords, indexInSuggestedWords));
- if (SuggestionStripView.DBG) {
- mDebugInfoViews.get(positionInStrip).setText(
- suggestedWords.getDebugString(indexInSuggestedWords));
- }
- count++;
- }
- return indexInSuggestedWords;
- }
-
- private int layoutPunctuationsAndReturnStartIndexOfMoreSuggestions(
- final PunctuationSuggestions punctuationSuggestions, final ViewGroup stripView) {
- final int countInStrip = Math.min(punctuationSuggestions.size(), PUNCTUATIONS_IN_STRIP);
- for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) {
- if (positionInStrip != 0) {
- // Add divider if this isn't the left most suggestion in suggestions strip.
- addDivider(stripView, mDividerViews.get(positionInStrip));
- }
-
- final TextView wordView = mWordViews.get(positionInStrip);
- final String punctuation = punctuationSuggestions.getLabel(positionInStrip);
- // {@link TextView#getTag()} is used to get the index in suggestedWords at
- // {@link SuggestionStripView#onClick(View)}.
- wordView.setTag(positionInStrip);
- wordView.setText(punctuation);
- wordView.setContentDescription(punctuation);
- wordView.setTextScaleX(1.0f);
- wordView.setCompoundDrawables(null, null, null, null);
- wordView.setTextColor(mColorAutoCorrect);
- stripView.addView(wordView);
- setLayoutWeight(wordView, 1.0f, mSuggestionsStripHeight);
- }
- mMoreSuggestionsAvailable = (punctuationSuggestions.size() > countInStrip);
- return countInStrip;
- }
-
- public void layoutImportantNotice(final View importantNoticeStrip,
- final String importantNoticeTitle) {
- final TextView titleView = (TextView)importantNoticeStrip.findViewById(
- R.id.important_notice_title);
- final int width = titleView.getWidth() - titleView.getPaddingLeft()
- - titleView.getPaddingRight();
- titleView.setTextColor(mColorAutoCorrect);
- titleView.setText(importantNoticeTitle); // TextView.setText() resets text scale x to 1.0.
- final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint());
- titleView.setTextScaleX(titleScaleX);
- }
-
- static void setLayoutWeight(final View v, final float weight, final int height) {
- final ViewGroup.LayoutParams lp = v.getLayoutParams();
- if (lp instanceof LinearLayout.LayoutParams) {
- final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
- llp.weight = weight;
- llp.width = 0;
- llp.height = height;
- }
- }
-
- private static float getTextScaleX(@Nullable final CharSequence text, final int maxWidth,
- final TextPaint paint) {
- paint.setTextScaleX(1.0f);
- final int width = getTextWidth(text, paint);
- if (width <= maxWidth || maxWidth <= 0) {
- return 1.0f;
- }
- return maxWidth / (float) width;
- }
-
- @Nullable
- private static CharSequence getEllipsizedTextWithSettingScaleX(
- @Nullable final CharSequence text, final int maxWidth, @Nonnull final TextPaint paint) {
- if (text == null) {
- return null;
- }
- final float scaleX = getTextScaleX(text, maxWidth, paint);
- if (scaleX >= MIN_TEXT_XSCALE) {
- paint.setTextScaleX(scaleX);
- return text;
- }
-
- // <code>text</code> must be ellipsized with minimum text scale x.
- paint.setTextScaleX(MIN_TEXT_XSCALE);
- final boolean hasBoldStyle = hasStyleSpan(text, BOLD_SPAN);
- final boolean hasUnderlineStyle = hasStyleSpan(text, UNDERLINE_SPAN);
- // TextUtils.ellipsize erases any span object existed after ellipsized point.
- // We have to restore these spans afterward.
- final CharSequence ellipsizedText = TextUtils.ellipsize(
- text, paint, maxWidth, TextUtils.TruncateAt.MIDDLE);
- if (!hasBoldStyle && !hasUnderlineStyle) {
- return ellipsizedText;
- }
- final Spannable spannableText = (ellipsizedText instanceof Spannable)
- ? (Spannable)ellipsizedText : new SpannableString(ellipsizedText);
- if (hasBoldStyle) {
- addStyleSpan(spannableText, BOLD_SPAN);
- }
- if (hasUnderlineStyle) {
- addStyleSpan(spannableText, UNDERLINE_SPAN);
- }
- return spannableText;
- }
-
- private static boolean hasStyleSpan(@Nullable final CharSequence text,
- final CharacterStyle style) {
- if (text instanceof Spanned) {
- return ((Spanned)text).getSpanStart(style) >= 0;
- }
- return false;
- }
-
- private static void addStyleSpan(@Nonnull final Spannable text, final CharacterStyle style) {
- text.removeSpan(style);
- text.setSpan(style, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- }
-
- private static int getTextWidth(@Nullable final CharSequence text, final TextPaint paint) {
- if (TextUtils.isEmpty(text)) {
- return 0;
- }
- final int length = text.length();
- final float[] widths = new float[length];
- final int count;
- final Typeface savedTypeface = paint.getTypeface();
- try {
- paint.setTypeface(getTextTypeface(text));
- count = paint.getTextWidths(text, 0, length, widths);
- } finally {
- paint.setTypeface(savedTypeface);
- }
- int width = 0;
- for (int i = 0; i < count; i++) {
- width += Math.round(widths[i] + 0.5f);
- }
- return width;
- }
-
- private static Typeface getTextTypeface(@Nullable final CharSequence text) {
- return hasStyleSpan(text, BOLD_SPAN) ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
deleted file mode 100644
index 5dba4928b..000000000
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.suggestions;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import androidx.core.view.ViewCompat;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageButton;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.keyboard.MoreKeysPanel;
-import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener;
-import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
-
-import java.util.ArrayList;
-
-public final class SuggestionStripView extends RelativeLayout implements OnClickListener,
- OnLongClickListener {
- public interface Listener {
- public void showImportantNoticeContents();
- public void pickSuggestionManually(SuggestedWordInfo word);
- public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat);
- }
-
- static final boolean DBG = DebugFlags.DEBUG_ENABLED;
- private static final float DEBUG_INFO_TEXT_SIZE_IN_DIP = 6.0f;
-
- private final ViewGroup mSuggestionsStrip;
- private final ImageButton mVoiceKey;
- private final View mImportantNoticeStrip;
- MainKeyboardView mMainKeyboardView;
-
- private final View mMoreSuggestionsContainer;
- private final MoreSuggestionsView mMoreSuggestionsView;
- private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
-
- private final ArrayList<TextView> mWordViews = new ArrayList<>();
- private final ArrayList<TextView> mDebugInfoViews = new ArrayList<>();
- private final ArrayList<View> mDividerViews = new ArrayList<>();
-
- Listener mListener;
- private SuggestedWords mSuggestedWords = SuggestedWords.getEmptyInstance();
- private int mStartIndexOfMoreSuggestions;
-
- private final SuggestionStripLayoutHelper mLayoutHelper;
- private final StripVisibilityGroup mStripVisibilityGroup;
-
- private static class StripVisibilityGroup {
- private final View mSuggestionStripView;
- private final View mSuggestionsStrip;
- private final View mImportantNoticeStrip;
-
- public StripVisibilityGroup(final View suggestionStripView,
- final ViewGroup suggestionsStrip, final View importantNoticeStrip) {
- mSuggestionStripView = suggestionStripView;
- mSuggestionsStrip = suggestionsStrip;
- mImportantNoticeStrip = importantNoticeStrip;
- showSuggestionsStrip();
- }
-
- public void setLayoutDirection(final boolean isRtlLanguage) {
- final int layoutDirection = isRtlLanguage ? ViewCompat.LAYOUT_DIRECTION_RTL
- : ViewCompat.LAYOUT_DIRECTION_LTR;
- ViewCompat.setLayoutDirection(mSuggestionStripView, layoutDirection);
- ViewCompat.setLayoutDirection(mSuggestionsStrip, layoutDirection);
- ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection);
- }
-
- public void showSuggestionsStrip() {
- mSuggestionsStrip.setVisibility(VISIBLE);
- mImportantNoticeStrip.setVisibility(INVISIBLE);
- }
-
- public void showImportantNoticeStrip() {
- mSuggestionsStrip.setVisibility(INVISIBLE);
- mImportantNoticeStrip.setVisibility(VISIBLE);
- }
-
- public boolean isShowingImportantNoticeStrip() {
- return mImportantNoticeStrip.getVisibility() == VISIBLE;
- }
- }
-
- /**
- * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
- * @param context
- * @param attrs
- */
- public SuggestionStripView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.suggestionStripViewStyle);
- }
-
- public SuggestionStripView(final Context context, final AttributeSet attrs,
- final int defStyle) {
- super(context, attrs, defStyle);
-
- final LayoutInflater inflater = LayoutInflater.from(context);
- inflater.inflate(R.layout.suggestions_strip, this);
-
- mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip);
- mVoiceKey = (ImageButton)findViewById(R.id.suggestions_strip_voice_key);
- mImportantNoticeStrip = findViewById(R.id.important_notice_strip);
- mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip,
- mImportantNoticeStrip);
-
- for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) {
- final TextView word = new TextView(context, null, R.attr.suggestionWordStyle);
- word.setContentDescription(getResources().getString(R.string.spoken_empty_suggestion));
- word.setOnClickListener(this);
- word.setOnLongClickListener(this);
- mWordViews.add(word);
- final View divider = inflater.inflate(R.layout.suggestion_divider, null);
- mDividerViews.add(divider);
- final TextView info = new TextView(context, null, R.attr.suggestionWordStyle);
- info.setTextColor(Color.WHITE);
- info.setTextSize(TypedValue.COMPLEX_UNIT_DIP, DEBUG_INFO_TEXT_SIZE_IN_DIP);
- mDebugInfoViews.add(info);
- }
-
- mLayoutHelper = new SuggestionStripLayoutHelper(
- context, attrs, defStyle, mWordViews, mDividerViews, mDebugInfoViews);
-
- mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
- mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
- .findViewById(R.id.more_suggestions_view);
- mMoreSuggestionsBuilder = new MoreSuggestions.Builder(context, mMoreSuggestionsView);
-
- final Resources res = context.getResources();
- mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
- R.dimen.config_more_suggestions_modal_tolerance);
- mMoreSuggestionsSlidingDetector = new GestureDetector(
- context, mMoreSuggestionsSlidingListener);
-
- final TypedArray keyboardAttr = context.obtainStyledAttributes(attrs,
- R.styleable.Keyboard, defStyle, R.style.SuggestionStripView);
- final Drawable iconVoice = keyboardAttr.getDrawable(R.styleable.Keyboard_iconShortcutKey);
- keyboardAttr.recycle();
- mVoiceKey.setImageDrawable(iconVoice);
- mVoiceKey.setOnClickListener(this);
- }
-
- /**
- * A connection back to the input method.
- * @param listener
- */
- public void setListener(final Listener listener, final View inputView) {
- mListener = listener;
- mMainKeyboardView = (MainKeyboardView)inputView.findViewById(R.id.keyboard_view);
- }
-
- public void updateVisibility(final boolean shouldBeVisible, final boolean isFullscreenMode) {
- final int visibility = shouldBeVisible ? VISIBLE : (isFullscreenMode ? GONE : INVISIBLE);
- setVisibility(visibility);
- final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
- mVoiceKey.setVisibility(currentSettingsValues.mShowsVoiceInputKey ? VISIBLE : INVISIBLE);
- }
-
- public void setSuggestions(final SuggestedWords suggestedWords, final boolean isRtlLanguage) {
- clear();
- mStripVisibilityGroup.setLayoutDirection(isRtlLanguage);
- mSuggestedWords = suggestedWords;
- mStartIndexOfMoreSuggestions = mLayoutHelper.layoutAndReturnStartIndexOfMoreSuggestions(
- getContext(), mSuggestedWords, mSuggestionsStrip, this);
- mStripVisibilityGroup.showSuggestionsStrip();
- }
-
- public void setMoreSuggestionsHeight(final int remainingHeight) {
- mLayoutHelper.setMoreSuggestionsHeight(remainingHeight);
- }
-
- // This method checks if we should show the important notice (checks on permanent storage if
- // it has been shown once already or not, and if in the setup wizard). If applicable, it shows
- // the notice. In all cases, it returns true if it was shown, false otherwise.
- public boolean maybeShowImportantNoticeTitle() {
- final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
- if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext(), currentSettingsValues)) {
- return false;
- }
- if (getWidth() <= 0) {
- return false;
- }
- final String importantNoticeTitle = ImportantNoticeUtils.getSuggestContactsNoticeTitle(
- getContext());
- if (TextUtils.isEmpty(importantNoticeTitle)) {
- return false;
- }
- if (isShowingMoreSuggestionPanel()) {
- dismissMoreSuggestionsPanel();
- }
- mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, importantNoticeTitle);
- mStripVisibilityGroup.showImportantNoticeStrip();
- mImportantNoticeStrip.setOnClickListener(this);
- return true;
- }
-
- public void clear() {
- mSuggestionsStrip.removeAllViews();
- removeAllDebugInfoViews();
- mStripVisibilityGroup.showSuggestionsStrip();
- dismissMoreSuggestionsPanel();
- }
-
- private void removeAllDebugInfoViews() {
- // The debug info views may be placed as children views of this {@link SuggestionStripView}.
- for (final View debugInfoView : mDebugInfoViews) {
- final ViewParent parent = debugInfoView.getParent();
- if (parent instanceof ViewGroup) {
- ((ViewGroup)parent).removeView(debugInfoView);
- }
- }
- }
-
- private final MoreSuggestionsListener mMoreSuggestionsListener = new MoreSuggestionsListener() {
- @Override
- public void onSuggestionSelected(final SuggestedWordInfo wordInfo) {
- mListener.pickSuggestionManually(wordInfo);
- dismissMoreSuggestionsPanel();
- }
-
- @Override
- public void onCancelInput() {
- dismissMoreSuggestionsPanel();
- }
- };
-
- private final MoreKeysPanel.Controller mMoreSuggestionsController =
- new MoreKeysPanel.Controller() {
- @Override
- public void onDismissMoreKeysPanel() {
- mMainKeyboardView.onDismissMoreKeysPanel();
- }
-
- @Override
- public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
- mMainKeyboardView.onShowMoreKeysPanel(panel);
- }
-
- @Override
- public void onCancelMoreKeysPanel() {
- dismissMoreSuggestionsPanel();
- }
- };
-
- public boolean isShowingMoreSuggestionPanel() {
- return mMoreSuggestionsView.isShowingInParent();
- }
-
- public void dismissMoreSuggestionsPanel() {
- mMoreSuggestionsView.dismissMoreKeysPanel();
- }
-
- @Override
- public boolean onLongClick(final View view) {
- AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
- Constants.NOT_A_CODE, this);
- return showMoreSuggestions();
- }
-
- boolean showMoreSuggestions() {
- final Keyboard parentKeyboard = mMainKeyboardView.getKeyboard();
- if (parentKeyboard == null) {
- return false;
- }
- final SuggestionStripLayoutHelper layoutHelper = mLayoutHelper;
- if (mSuggestedWords.size() <= mStartIndexOfMoreSuggestions) {
- return false;
- }
- final int stripWidth = getWidth();
- final View container = mMoreSuggestionsContainer;
- final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
- final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
- builder.layout(mSuggestedWords, mStartIndexOfMoreSuggestions, maxWidth,
- (int)(maxWidth * layoutHelper.mMinMoreSuggestionsWidth),
- layoutHelper.getMaxMoreSuggestionsRow(), parentKeyboard);
- mMoreSuggestionsView.setKeyboard(builder.build());
- container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
- final int pointX = stripWidth / 2;
- final int pointY = -layoutHelper.mMoreSuggestionsBottomGap;
- moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
- mMoreSuggestionsListener);
- mOriginX = mLastX;
- mOriginY = mLastY;
- for (int i = 0; i < mStartIndexOfMoreSuggestions; i++) {
- mWordViews.get(i).setPressed(false);
- }
- return true;
- }
-
- // Working variables for {@link onInterceptTouchEvent(MotionEvent)} and
- // {@link onTouchEvent(MotionEvent)}.
- private int mLastX;
- private int mLastY;
- private int mOriginX;
- private int mOriginY;
- private final int mMoreSuggestionsModalTolerance;
- private boolean mNeedsToTransformTouchEventToHoverEvent;
- private boolean mIsDispatchingHoverEventToMoreSuggestions;
- private final GestureDetector mMoreSuggestionsSlidingDetector;
- private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener =
- new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onScroll(MotionEvent down, MotionEvent me, float deltaX, float deltaY) {
- if (down == null) {
- return false;
- }
- final float dy = me.getY() - down.getY();
- if (deltaY > 0 && dy < 0) {
- return showMoreSuggestions();
- }
- return false;
- }
- };
-
- @Override
- public boolean onInterceptTouchEvent(final MotionEvent me) {
- if (mStripVisibilityGroup.isShowingImportantNoticeStrip()) {
- return false;
- }
- // Detecting sliding up finger to show {@link MoreSuggestionsView}.
- if (!mMoreSuggestionsView.isShowingInParent()) {
- mLastX = (int)me.getX();
- mLastY = (int)me.getY();
- return mMoreSuggestionsSlidingDetector.onTouchEvent(me);
- }
- if (mMoreSuggestionsView.isInModalMode()) {
- return false;
- }
-
- final int action = me.getAction();
- final int index = me.getActionIndex();
- final int x = (int)me.getX(index);
- final int y = (int)me.getY(index);
- if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
- || mOriginY - y >= mMoreSuggestionsModalTolerance) {
- // Decided to be in the sliding suggestion mode only when the touch point has been moved
- // upward. Further {@link MotionEvent}s will be delivered to
- // {@link #onTouchEvent(MotionEvent)}.
- mNeedsToTransformTouchEventToHoverEvent =
- AccessibilityUtils.getInstance().isTouchExplorationEnabled();
- mIsDispatchingHoverEventToMoreSuggestions = false;
- return true;
- }
-
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
- // Decided to be in the modal input mode.
- mMoreSuggestionsView.setModalMode();
- }
- return false;
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
- // Don't populate accessibility event with suggested words and voice key.
- return true;
- }
-
- @Override
- public boolean onTouchEvent(final MotionEvent me) {
- if (!mMoreSuggestionsView.isShowingInParent()) {
- // Ignore any touch event while more suggestions panel hasn't been shown.
- // Detecting sliding up is done at {@link #onInterceptTouchEvent}.
- return true;
- }
- // In the sliding input mode. {@link MotionEvent} should be forwarded to
- // {@link MoreSuggestionsView}.
- final int index = me.getActionIndex();
- final int x = mMoreSuggestionsView.translateX((int)me.getX(index));
- final int y = mMoreSuggestionsView.translateY((int)me.getY(index));
- me.setLocation(x, y);
- if (!mNeedsToTransformTouchEventToHoverEvent) {
- mMoreSuggestionsView.onTouchEvent(me);
- return true;
- }
- // In sliding suggestion mode with accessibility mode on, a touch event should be
- // transformed to a hover event.
- final int width = mMoreSuggestionsView.getWidth();
- final int height = mMoreSuggestionsView.getHeight();
- final boolean onMoreSuggestions = (x >= 0 && x < width && y >= 0 && y < height);
- if (!onMoreSuggestions && !mIsDispatchingHoverEventToMoreSuggestions) {
- // Just drop this touch event because dispatching hover event isn't started yet and
- // the touch event isn't on {@link MoreSuggestionsView}.
- return true;
- }
- final int hoverAction;
- if (onMoreSuggestions && !mIsDispatchingHoverEventToMoreSuggestions) {
- // Transform this touch event to a hover enter event and start dispatching a hover
- // event to {@link MoreSuggestionsView}.
- mIsDispatchingHoverEventToMoreSuggestions = true;
- hoverAction = MotionEvent.ACTION_HOVER_ENTER;
- } else if (me.getActionMasked() == MotionEvent.ACTION_UP) {
- // Transform this touch event to a hover exit event and stop dispatching a hover event
- // after this.
- mIsDispatchingHoverEventToMoreSuggestions = false;
- mNeedsToTransformTouchEventToHoverEvent = false;
- hoverAction = MotionEvent.ACTION_HOVER_EXIT;
- } else {
- // Transform this touch event to a hover move event.
- hoverAction = MotionEvent.ACTION_HOVER_MOVE;
- }
- me.setAction(hoverAction);
- mMoreSuggestionsView.onHoverEvent(me);
- return true;
- }
-
- @Override
- public void onClick(final View view) {
- AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
- Constants.CODE_UNSPECIFIED, this);
- if (view == mImportantNoticeStrip) {
- mListener.showImportantNoticeContents();
- return;
- }
- if (view == mVoiceKey) {
- mListener.onCodeInput(Constants.CODE_SHORTCUT,
- Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
- false /* isKeyRepeat */);
- return;
- }
-
- final Object tag = view.getTag();
- // {@link Integer} tag is set at
- // {@link SuggestionStripLayoutHelper#setupWordViewsTextAndColor(SuggestedWords,int)} and
- // {@link SuggestionStripLayoutHelper#layoutPunctuationSuggestions(SuggestedWords,ViewGroup}
- if (tag instanceof Integer) {
- final int index = (Integer) tag;
- if (index >= mSuggestedWords.size()) {
- return;
- }
- final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(index);
- mListener.pickSuggestionManually(wordInfo);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- dismissMoreSuggestionsPanel();
- }
-
- @Override
- protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
- // Called by the framework when the size is known. Show the important notice if applicable.
- // This may be overriden by showing suggestions later, if applicable.
- if (oldw <= 0 && w > 0) {
- maybeShowImportantNoticeTitle();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
deleted file mode 100644
index 68f417e84..000000000
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.suggestions;
-
-import com.android.inputmethod.latin.SuggestedWords;
-
-/**
- * An object that gives basic control of a suggestion strip and some info on it.
- */
-public interface SuggestionStripViewAccessor {
- public void setNeutralSuggestionStrip();
- public void showSuggestionStrip(final SuggestedWords suggestedWords);
-}
diff --git a/java/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java b/java/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java
deleted file mode 100644
index 8291b4f72..000000000
--- a/java/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.touchinputconsumer;
-
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.DictionaryFacilitator;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.common.InputPointers;
-import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer;
-
-import java.util.Locale;
-
-/**
- * Stub for GestureConsumer.
- * <br>
- * The methods of this class should only be called from a single thread, e.g.,
- * the UI Thread.
- */
-@SuppressWarnings("unused")
-public class GestureConsumer {
- public static final GestureConsumer NULL_GESTURE_CONSUMER =
- new GestureConsumer();
-
- public static GestureConsumer newInstance(
- final EditorInfo editorInfo, final PrivateCommandPerformer commandPerformer,
- final Locale locale, final Keyboard keyboard) {
- return GestureConsumer.NULL_GESTURE_CONSUMER;
- }
-
- private GestureConsumer() {
- }
-
- public boolean willConsume() {
- return false;
- }
-
- public void onInit(final Locale locale, final Keyboard keyboard) {
- }
-
- public void onGestureStarted(final Locale locale, final Keyboard keyboard) {
- }
-
- public void onGestureCanceled() {
- }
-
- public void onGestureCompleted(final InputPointers inputPointers) {
- }
-
- public void onImeSuggestionsProcessed(final SuggestedWords suggestedWords,
- final int composingStart, final int composingLength,
- final DictionaryFacilitator dictionaryFacilitator) {
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
deleted file mode 100644
index 58e6a25b8..000000000
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.userdictionary;
-
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.provider.UserDictionary;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.EditText;
-
-import com.android.inputmethod.compat.UserDictionaryCompatUtils;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.TreeSet;
-
-import javax.annotation.Nullable;
-
-// Caveat: This class is basically taken from
-// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryAddWordContents.java
-// in order to deal with some devices that have issues with the user dictionary handling
-
-/**
- * A container class to factor common code to UserDictionaryAddWordFragment
- * and UserDictionaryAddWordActivity.
- */
-public class UserDictionaryAddWordContents {
- public static final String EXTRA_MODE = "mode";
- public static final String EXTRA_WORD = "word";
- public static final String EXTRA_SHORTCUT = "shortcut";
- public static final String EXTRA_LOCALE = "locale";
- public static final String EXTRA_ORIGINAL_WORD = "originalWord";
- public static final String EXTRA_ORIGINAL_SHORTCUT = "originalShortcut";
-
- public static final int MODE_EDIT = 0;
- public static final int MODE_INSERT = 1;
-
- /* package */ static final int CODE_WORD_ADDED = 0;
- /* package */ static final int CODE_CANCEL = 1;
- /* package */ static final int CODE_ALREADY_PRESENT = 2;
-
- private static final int FREQUENCY_FOR_USER_DICTIONARY_ADDS = 250;
-
- private final int mMode; // Either MODE_EDIT or MODE_INSERT
- private final EditText mWordEditText;
- private final EditText mShortcutEditText;
- private String mLocale;
- private final String mOldWord;
- private final String mOldShortcut;
- private String mSavedWord;
- private String mSavedShortcut;
-
- /* package */ UserDictionaryAddWordContents(final View view, final Bundle args) {
- mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
- mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut);
- if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
- mShortcutEditText.setVisibility(View.GONE);
- view.findViewById(R.id.user_dictionary_add_shortcut_label).setVisibility(View.GONE);
- }
- final String word = args.getString(EXTRA_WORD);
- if (null != word) {
- mWordEditText.setText(word);
- // Use getText in case the edit text modified the text we set. This happens when
- // it's too long to be edited.
- mWordEditText.setSelection(mWordEditText.getText().length());
- }
- final String shortcut;
- if (UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
- shortcut = args.getString(EXTRA_SHORTCUT);
- if (null != shortcut && null != mShortcutEditText) {
- mShortcutEditText.setText(shortcut);
- }
- mOldShortcut = args.getString(EXTRA_SHORTCUT);
- } else {
- shortcut = null;
- mOldShortcut = null;
- }
- mMode = args.getInt(EXTRA_MODE); // default return value for #getInt() is 0 = MODE_EDIT
- mOldWord = args.getString(EXTRA_WORD);
- updateLocale(args.getString(EXTRA_LOCALE));
- }
-
- /* package */ UserDictionaryAddWordContents(final View view,
- final UserDictionaryAddWordContents oldInstanceToBeEdited) {
- mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
- mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut);
- mMode = MODE_EDIT;
- mOldWord = oldInstanceToBeEdited.mSavedWord;
- mOldShortcut = oldInstanceToBeEdited.mSavedShortcut;
- updateLocale(mLocale);
- }
-
- // locale may be null, this means default locale
- // It may also be the empty string, which means "all locales"
- /* package */ void updateLocale(final String locale) {
- mLocale = null == locale ? Locale.getDefault().toString() : locale;
- }
-
- /* package */ void saveStateIntoBundle(final Bundle outState) {
- outState.putString(EXTRA_WORD, mWordEditText.getText().toString());
- outState.putString(EXTRA_ORIGINAL_WORD, mOldWord);
- if (null != mShortcutEditText) {
- outState.putString(EXTRA_SHORTCUT, mShortcutEditText.getText().toString());
- }
- if (null != mOldShortcut) {
- outState.putString(EXTRA_ORIGINAL_SHORTCUT, mOldShortcut);
- }
- outState.putString(EXTRA_LOCALE, mLocale);
- }
-
- /* package */ void delete(final Context context) {
- if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
- // Mode edit: remove the old entry.
- final ContentResolver resolver = context.getContentResolver();
- UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
- }
- // If we are in add mode, nothing was added, so we don't need to do anything.
- }
-
- /* package */
- int apply(final Context context, final Bundle outParameters) {
- if (null != outParameters) saveStateIntoBundle(outParameters);
- final ContentResolver resolver = context.getContentResolver();
- if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
- // Mode edit: remove the old entry.
- UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
- }
- final String newWord = mWordEditText.getText().toString();
- final String newShortcut;
- if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
- newShortcut = null;
- } else if (null == mShortcutEditText) {
- newShortcut = null;
- } else {
- final String tmpShortcut = mShortcutEditText.getText().toString();
- if (TextUtils.isEmpty(tmpShortcut)) {
- newShortcut = null;
- } else {
- newShortcut = tmpShortcut;
- }
- }
- if (TextUtils.isEmpty(newWord)) {
- // If the word is somehow empty, don't insert it.
- return CODE_CANCEL;
- }
- mSavedWord = newWord;
- mSavedShortcut = newShortcut;
- // If there is no shortcut, and the word already exists in the database, then we
- // should not insert, because either A. the word exists with no shortcut, in which
- // case the exact same thing we want to insert is already there, or B. the word
- // exists with at least one shortcut, in which case it has priority on our word.
- if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) {
- return CODE_ALREADY_PRESENT;
- }
-
- // Disallow duplicates. If the same word with no shortcut is defined, remove it; if
- // the same word with the same shortcut is defined, remove it; but we don't mind if
- // there is the same word with a different, non-empty shortcut.
- UserDictionarySettings.deleteWord(newWord, null, resolver);
- if (!TextUtils.isEmpty(newShortcut)) {
- // If newShortcut is empty we just deleted this, no need to do it again
- UserDictionarySettings.deleteWord(newWord, newShortcut, resolver);
- }
-
- // In this class we use the empty string to represent 'all locales' and mLocale cannot
- // be null. However the addWord method takes null to mean 'all locales'.
- UserDictionaryCompatUtils.addWord(context, newWord.toString(),
- FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut, TextUtils.isEmpty(mLocale) ?
- null : LocaleUtils.constructLocaleFromString(mLocale));
-
- return CODE_WORD_ADDED;
- }
-
- private static final String[] HAS_WORD_PROJECTION = { UserDictionary.Words.WORD };
- private static final String HAS_WORD_SELECTION_ONE_LOCALE = UserDictionary.Words.WORD
- + "=? AND " + UserDictionary.Words.LOCALE + "=?";
- private static final String HAS_WORD_SELECTION_ALL_LOCALES = UserDictionary.Words.WORD
- + "=? AND " + UserDictionary.Words.LOCALE + " is null";
- private boolean hasWord(final String word, final Context context) {
- final Cursor cursor;
- // mLocale == "" indicates this is an entry for all languages. Here, mLocale can't
- // be null at all (it's ensured by the updateLocale method).
- if ("".equals(mLocale)) {
- cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
- HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ALL_LOCALES,
- new String[] { word }, null /* sort order */);
- } else {
- cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
- HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ONE_LOCALE,
- new String[] { word, mLocale }, null /* sort order */);
- }
- try {
- if (null == cursor) return false;
- return cursor.getCount() > 0;
- } finally {
- if (null != cursor) cursor.close();
- }
- }
-
- public static class LocaleRenderer {
- private final String mLocaleString;
- private final String mDescription;
-
- public LocaleRenderer(final Context context, @Nullable final String localeString) {
- mLocaleString = localeString;
- if (null == localeString) {
- mDescription = context.getString(R.string.user_dict_settings_more_languages);
- } else if ("".equals(localeString)) {
- mDescription = context.getString(R.string.user_dict_settings_all_languages);
- } else {
- mDescription = LocaleUtils.constructLocaleFromString(localeString).getDisplayName();
- }
- }
- @Override
- public String toString() {
- return mDescription;
- }
- public String getLocaleString() {
- return mLocaleString;
- }
- // "More languages..." is null ; "All languages" is the empty string.
- public boolean isMoreLanguages() {
- return null == mLocaleString;
- }
- }
-
- private static void addLocaleDisplayNameToList(final Context context,
- final ArrayList<LocaleRenderer> list, final String locale) {
- if (null != locale) {
- list.add(new LocaleRenderer(context, locale));
- }
- }
-
- // Helper method to get the list of locales to display for this word
- public ArrayList<LocaleRenderer> getLocalesList(final Activity activity) {
- final TreeSet<String> locales = UserDictionaryList.getUserDictionaryLocalesSet(activity);
- // Remove our locale if it's in, because we're always gonna put it at the top
- locales.remove(mLocale); // mLocale may not be null
- final String systemLocale = Locale.getDefault().toString();
- // The system locale should be inside. We want it at the 2nd spot.
- locales.remove(systemLocale); // system locale may not be null
- locales.remove(""); // Remove the empty string if it's there
- final ArrayList<LocaleRenderer> localesList = new ArrayList<>();
- // Add the passed locale, then the system locale at the top of the list. Add an
- // "all languages" entry at the bottom of the list.
- addLocaleDisplayNameToList(activity, localesList, mLocale);
- if (!systemLocale.equals(mLocale)) {
- addLocaleDisplayNameToList(activity, localesList, systemLocale);
- }
- for (final String l : locales) {
- // TODO: sort in unicode order
- addLocaleDisplayNameToList(activity, localesList, l);
- }
- if (!"".equals(mLocale)) {
- // If mLocale is "", then we already inserted the "all languages" item, so don't do it
- addLocaleDisplayNameToList(activity, localesList, ""); // meaning: all languages
- }
- localesList.add(new LocaleRenderer(activity, null)); // meaning: select another locale
- return localesList;
- }
-
- public String getCurrentUserDictionaryLocale() {
- return mLocale;
- }
-}
-
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
deleted file mode 100644
index a8781d786..000000000
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.userdictionary;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordContents.LocaleRenderer;
-import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker.LocationChangedListener;
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-
-import java.util.ArrayList;
-import java.util.Locale;
-
-// Caveat: This class is basically taken from
-// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
-// in order to deal with some devices that have issues with the user dictionary handling
-
-/**
- * Fragment to add a word/shortcut to the user dictionary.
- *
- * As opposed to the UserDictionaryActivity, this is only invoked within Settings
- * from the UserDictionarySettings.
- */
-public class UserDictionaryAddWordFragment extends Fragment
- implements AdapterView.OnItemSelectedListener, LocationChangedListener {
-
- private static final int OPTIONS_MENU_ADD = Menu.FIRST;
- private static final int OPTIONS_MENU_DELETE = Menu.FIRST + 1;
-
- private UserDictionaryAddWordContents mContents;
- private View mRootView;
- private boolean mIsDeleting = false;
-
- @Override
- public void onActivityCreated(final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setHasOptionsMenu(true);
- getActivity().getActionBar().setTitle(R.string.edit_personal_dictionary);
- // Keep the instance so that we remember mContents when configuration changes (eg rotation)
- setRetainInstance(true);
- }
-
- @Override
- public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
- final Bundle savedState) {
- mRootView = inflater.inflate(R.layout.user_dictionary_add_word_fullscreen, null);
- mIsDeleting = false;
- // If we have a non-null mContents object, it's the old value before a configuration
- // change (eg rotation) so we need to use its values. Otherwise, read from the arguments.
- if (null == mContents) {
- mContents = new UserDictionaryAddWordContents(mRootView, getArguments());
- } else {
- // We create a new mContents object to account for the new situation : a word has
- // been added to the user dictionary when we started rotating, and we are now editing
- // it. That means in particular if the word undergoes any change, the old version should
- // be updated, so the mContents object needs to switch to EDIT mode if it was in
- // INSERT mode.
- mContents = new UserDictionaryAddWordContents(mRootView,
- mContents /* oldInstanceToBeEdited */);
- }
- getActivity().getActionBar().setSubtitle(UserDictionarySettingsUtils.getLocaleDisplayName(
- getActivity(), mContents.getCurrentUserDictionaryLocale()));
- return mRootView;
- }
-
- @Override
- public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
- final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0,
- R.string.user_dict_settings_add_menu_title).setIcon(R.drawable.ic_menu_add);
- actionItemAdd.setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- final MenuItem actionItemDelete = menu.add(0, OPTIONS_MENU_DELETE, 0,
- R.string.user_dict_settings_delete).setIcon(android.R.drawable.ic_menu_delete);
- actionItemDelete.setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- }
-
- /**
- * Callback for the framework when a menu option is pressed.
- *
- * @param item the item that was pressed
- * @return false to allow normal menu processing to proceed, true to consume it here
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == OPTIONS_MENU_ADD) {
- // added the entry in "onPause"
- getActivity().onBackPressed();
- return true;
- }
- if (item.getItemId() == OPTIONS_MENU_DELETE) {
- mContents.delete(getActivity());
- mIsDeleting = true;
- getActivity().onBackPressed();
- return true;
- }
- return false;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- // We are being shown: display the word
- updateSpinner();
- }
-
- private void updateSpinner() {
- final ArrayList<LocaleRenderer> localesList = mContents.getLocalesList(getActivity());
-
- final Spinner localeSpinner =
- (Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale);
- final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<>(
- getActivity(), android.R.layout.simple_spinner_item, localesList);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- localeSpinner.setAdapter(adapter);
- localeSpinner.setOnItemSelectedListener(this);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- // We are being hidden: commit changes to the user dictionary, unless we were deleting it
- if (!mIsDeleting) {
- mContents.apply(getActivity(), null);
- }
- }
-
- @Override
- public void onItemSelected(final AdapterView<?> parent, final View view, final int pos,
- final long id) {
- final LocaleRenderer locale = (LocaleRenderer)parent.getItemAtPosition(pos);
- if (locale.isMoreLanguages()) {
- PreferenceActivity preferenceActivity = (PreferenceActivity)getActivity();
- preferenceActivity.startPreferenceFragment(new UserDictionaryLocalePicker(), true);
- } else {
- mContents.updateLocale(locale.getLocaleString());
- }
- }
-
- @Override
- public void onNothingSelected(final AdapterView<?> parent) {
- // I'm not sure we can come here, but if we do, that's the right thing to do.
- final Bundle args = getArguments();
- mContents.updateLocale(args.getString(UserDictionaryAddWordContents.EXTRA_LOCALE));
- }
-
- // Called by the locale picker
- @Override
- public void onLocaleSelected(final Locale locale) {
- mContents.updateLocale(locale.toString());
- getActivity().onBackPressed();
- }
-}
-
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
deleted file mode 100644
index f7940e3de..000000000
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.userdictionary;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceGroup;
-import android.provider.UserDictionary;
-import android.text.TextUtils;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import java.util.List;
-import java.util.Locale;
-import java.util.TreeSet;
-
-import javax.annotation.Nullable;
-
-// Caveat: This class is basically taken from
-// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryList.java
-// in order to deal with some devices that have issues with the user dictionary handling
-
-public class UserDictionaryList extends PreferenceFragment {
-
- public static final String USER_DICTIONARY_SETTINGS_INTENT_ACTION =
- "android.settings.USER_DICTIONARY_SETTINGS";
-
- @Override
- public void onCreate(final Bundle icicle) {
- super.onCreate(icicle);
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity()));
- }
-
- public static TreeSet<String> getUserDictionaryLocalesSet(final Activity activity) {
- final Cursor cursor = activity.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
- new String[] { UserDictionary.Words.LOCALE },
- null, null, null);
- final TreeSet<String> localeSet = new TreeSet<>();
- if (null == cursor) {
- // The user dictionary service is not present or disabled. Return null.
- return null;
- }
- try {
- if (cursor.moveToFirst()) {
- final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
- do {
- final String locale = cursor.getString(columnIndex);
- localeSet.add(null != locale ? locale : "");
- } while (cursor.moveToNext());
- }
- } finally {
- cursor.close();
- }
- if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
- // For ICS, we need to show "For all languages" in case that the keyboard locale
- // is different from the system locale
- localeSet.add("");
- }
-
- final InputMethodManager imm =
- (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
- final List<InputMethodInfo> imis = imm.getEnabledInputMethodList();
- for (final InputMethodInfo imi : imis) {
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(
- imi, true /* allowsImplicitlySelectedSubtypes */);
- for (InputMethodSubtype subtype : subtypes) {
- final String locale = subtype.getLocale();
- if (!TextUtils.isEmpty(locale)) {
- localeSet.add(locale);
- }
- }
- }
-
- // We come here after we have collected locales from existing user dictionary entries and
- // enabled subtypes. If we already have the locale-without-country version of the system
- // locale, we don't add the system locale to avoid confusion even though it's technically
- // correct to add it.
- if (!localeSet.contains(Locale.getDefault().getLanguage().toString())) {
- localeSet.add(Locale.getDefault().toString());
- }
-
- return localeSet;
- }
-
- /**
- * Creates the entries that allow the user to go into the user dictionary for each locale.
- * @param userDictGroup The group to put the settings in.
- */
- protected void createUserDictSettings(final PreferenceGroup userDictGroup) {
- final Activity activity = getActivity();
- userDictGroup.removeAll();
- final TreeSet<String> localeSet =
- UserDictionaryList.getUserDictionaryLocalesSet(activity);
-
- if (localeSet.size() > 1) {
- // Have an "All languages" entry in the languages list if there are two or more active
- // languages
- localeSet.add("");
- }
-
- if (localeSet.isEmpty()) {
- userDictGroup.addPreference(createUserDictionaryPreference(null));
- } else {
- for (String locale : localeSet) {
- userDictGroup.addPreference(createUserDictionaryPreference(locale));
- }
- }
- }
-
- /**
- * Create a single User Dictionary Preference object, with its parameters set.
- * @param localeString The locale for which this user dictionary is for.
- * @return The corresponding preference.
- */
- protected Preference createUserDictionaryPreference(@Nullable final String localeString) {
- final Preference newPref = new Preference(getActivity());
- final Intent intent = new Intent(USER_DICTIONARY_SETTINGS_INTENT_ACTION);
- if (null == localeString) {
- newPref.setTitle(Locale.getDefault().getDisplayName());
- } else {
- if (localeString.isEmpty()) {
- newPref.setTitle(getString(R.string.user_dict_settings_all_languages));
- } else {
- newPref.setTitle(
- LocaleUtils.constructLocaleFromString(localeString).getDisplayName());
- }
- intent.putExtra("locale", localeString);
- newPref.getExtras().putString("locale", localeString);
- }
- newPref.setIntent(intent);
- newPref.setFragment(UserDictionarySettings.class.getName());
- return newPref;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- createUserDictSettings(getPreferenceScreen());
- }
-}
-
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java
deleted file mode 100644
index 58d3fb91c..000000000
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryLocalePicker.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.userdictionary;
-
-import android.app.Fragment;
-
-import java.util.Locale;
-
-// Caveat: This class is basically taken from
-// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionaryLocalePicker.java
-// in order to deal with some devices that have issues with the user dictionary handling
-
-public class UserDictionaryLocalePicker extends Fragment {
- public UserDictionaryLocalePicker() {
- super();
- // TODO: implement
- }
-
- public interface LocationChangedListener {
- public void onLocaleSelected(Locale locale);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
deleted file mode 100644
index d9339d965..000000000
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.userdictionary;
-
-import com.android.inputmethod.latin.R;
-
-import android.app.ListFragment;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.Build;
-import android.os.Bundle;
-import android.provider.UserDictionary;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AlphabetIndexer;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.SectionIndexer;
-import android.widget.SimpleCursorAdapter;
-import android.widget.TextView;
-
-import java.util.Locale;
-
-// Caveat: This class is basically taken from
-// packages/apps/Settings/src/com/android/settings/inputmethod/UserDictionarySettings.java
-// in order to deal with some devices that have issues with the user dictionary handling
-
-public class UserDictionarySettings extends ListFragment {
-
- public static final boolean IS_SHORTCUT_API_SUPPORTED =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
-
- private static final String[] QUERY_PROJECTION_SHORTCUT_UNSUPPORTED =
- { UserDictionary.Words._ID, UserDictionary.Words.WORD};
- private static final String[] QUERY_PROJECTION_SHORTCUT_SUPPORTED =
- { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT};
- private static final String[] QUERY_PROJECTION =
- IS_SHORTCUT_API_SUPPORTED ?
- QUERY_PROJECTION_SHORTCUT_SUPPORTED : QUERY_PROJECTION_SHORTCUT_UNSUPPORTED;
-
- // The index of the shortcut in the above array.
- private static final int INDEX_SHORTCUT = 2;
-
- private static final String[] ADAPTER_FROM_SHORTCUT_UNSUPPORTED = {
- UserDictionary.Words.WORD,
- };
-
- private static final String[] ADAPTER_FROM_SHORTCUT_SUPPORTED = {
- UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT
- };
-
- private static final String[] ADAPTER_FROM = IS_SHORTCUT_API_SUPPORTED ?
- ADAPTER_FROM_SHORTCUT_SUPPORTED : ADAPTER_FROM_SHORTCUT_UNSUPPORTED;
-
- private static final int[] ADAPTER_TO_SHORTCUT_UNSUPPORTED = {
- android.R.id.text1,
- };
-
- private static final int[] ADAPTER_TO_SHORTCUT_SUPPORTED = {
- android.R.id.text1, android.R.id.text2
- };
-
- private static final int[] ADAPTER_TO = IS_SHORTCUT_API_SUPPORTED ?
- ADAPTER_TO_SHORTCUT_SUPPORTED : ADAPTER_TO_SHORTCUT_UNSUPPORTED;
-
- // Either the locale is empty (means the word is applicable to all locales)
- // or the word equals our current locale
- private static final String QUERY_SELECTION =
- UserDictionary.Words.LOCALE + "=?";
- private static final String QUERY_SELECTION_ALL_LOCALES =
- UserDictionary.Words.LOCALE + " is null";
-
- private static final String DELETE_SELECTION_WITH_SHORTCUT = UserDictionary.Words.WORD
- + "=? AND " + UserDictionary.Words.SHORTCUT + "=?";
- private static final String DELETE_SELECTION_WITHOUT_SHORTCUT = UserDictionary.Words.WORD
- + "=? AND " + UserDictionary.Words.SHORTCUT + " is null OR "
- + UserDictionary.Words.SHORTCUT + "=''";
- private static final String DELETE_SELECTION_SHORTCUT_UNSUPPORTED =
- UserDictionary.Words.WORD + "=?";
-
- private static final int OPTIONS_MENU_ADD = Menu.FIRST;
-
- private Cursor mCursor;
-
- protected String mLocale;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getActivity().getActionBar().setTitle(R.string.edit_personal_dictionary);
- }
-
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(
- R.layout.user_dictionary_preference_list_fragment, container, false);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- final Intent intent = getActivity().getIntent();
- final String localeFromIntent =
- null == intent ? null : intent.getStringExtra("locale");
-
- final Bundle arguments = getArguments();
- final String localeFromArguments =
- null == arguments ? null : arguments.getString("locale");
-
- final String locale;
- if (null != localeFromArguments) {
- locale = localeFromArguments;
- } else if (null != localeFromIntent) {
- locale = localeFromIntent;
- } else {
- locale = null;
- }
-
- mLocale = locale;
- // WARNING: The following cursor is never closed! TODO: don't put that in a member, and
- // make sure all cursors are correctly closed. Also, this comes from a call to
- // Activity#managedQuery, which has been deprecated for a long time (and which FORBIDS
- // closing the cursor, so take care when resolving this TODO). We should either use a
- // regular query and close the cursor, or switch to a LoaderManager and a CursorLoader.
- mCursor = createCursor(locale);
- TextView emptyView = (TextView) getView().findViewById(android.R.id.empty);
- emptyView.setText(R.string.user_dict_settings_empty_text);
-
- final ListView listView = getListView();
- listView.setAdapter(createAdapter());
- listView.setFastScrollEnabled(true);
- listView.setEmptyView(emptyView);
-
- setHasOptionsMenu(true);
- // Show the language as a subtitle of the action bar
- getActivity().getActionBar().setSubtitle(
- UserDictionarySettingsUtils.getLocaleDisplayName(getActivity(), mLocale));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- ListAdapter adapter = getListView().getAdapter();
- if (adapter != null && adapter instanceof MyAdapter) {
- // The list view is forced refreshed here. This allows the changes done
- // in UserDictionaryAddWordFragment (update/delete/insert) to be seen when
- // user goes back to this view.
- MyAdapter listAdapter = (MyAdapter) adapter;
- listAdapter.notifyDataSetChanged();
- }
- }
-
- @SuppressWarnings("deprecation")
- private Cursor createCursor(final String locale) {
- // Locale can be any of:
- // - The string representation of a locale, as returned by Locale#toString()
- // - The empty string. This means we want a cursor returning words valid for all locales.
- // - null. This means we want a cursor for the current locale, whatever this is.
- // Note that this contrasts with the data inside the database, where NULL means "all
- // locales" and there should never be an empty string. The confusion is called by the
- // historical use of null for "all locales".
- // TODO: it should be easy to make this more readable by making the special values
- // human-readable, like "all_locales" and "current_locales" strings, provided they
- // can be guaranteed not to match locales that may exist.
- if ("".equals(locale)) {
- // Case-insensitive sort
- return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
- QUERY_SELECTION_ALL_LOCALES, null,
- "UPPER(" + UserDictionary.Words.WORD + ")");
- }
- final String queryLocale = null != locale ? locale : Locale.getDefault().toString();
- return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
- QUERY_SELECTION, new String[] { queryLocale },
- "UPPER(" + UserDictionary.Words.WORD + ")");
- }
-
- private ListAdapter createAdapter() {
- return new MyAdapter(getActivity(), R.layout.user_dictionary_item, mCursor,
- ADAPTER_FROM, ADAPTER_TO);
- }
-
- @Override
- public void onListItemClick(ListView l, View v, int position, long id) {
- final String word = getWord(position);
- final String shortcut = getShortcut(position);
- if (word != null) {
- showAddOrEditDialog(word, shortcut);
- }
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
- final Locale systemLocale = getResources().getConfiguration().locale;
- if (!TextUtils.isEmpty(mLocale) && !mLocale.equals(systemLocale.toString())) {
- // Hide the add button for ICS because it doesn't support specifying a locale
- // for an entry. This new "locale"-aware API has been added in conjunction
- // with the shortcut API.
- return;
- }
- }
- MenuItem actionItem =
- menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
- .setIcon(R.drawable.ic_menu_add);
- actionItem.setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == OPTIONS_MENU_ADD) {
- showAddOrEditDialog(null, null);
- return true;
- }
- return false;
- }
-
- /**
- * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit.
- * @param editingWord the word to edit, or null if it's an add.
- * @param editingShortcut the shortcut for this entry, or null if none.
- */
- private void showAddOrEditDialog(final String editingWord, final String editingShortcut) {
- final Bundle args = new Bundle();
- args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, null == editingWord
- ? UserDictionaryAddWordContents.MODE_INSERT
- : UserDictionaryAddWordContents.MODE_EDIT);
- args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord);
- args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut);
- args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale);
- android.preference.PreferenceActivity pa =
- (android.preference.PreferenceActivity)getActivity();
- pa.startPreferencePanel(UserDictionaryAddWordFragment.class.getName(),
- args, R.string.user_dict_settings_add_dialog_title, null, null, 0);
- }
-
- private String getWord(final int position) {
- if (null == mCursor) return null;
- mCursor.moveToPosition(position);
- // Handle a possible race-condition
- if (mCursor.isAfterLast()) return null;
-
- return mCursor.getString(
- mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
- }
-
- private String getShortcut(final int position) {
- if (!IS_SHORTCUT_API_SUPPORTED) return null;
- if (null == mCursor) return null;
- mCursor.moveToPosition(position);
- // Handle a possible race-condition
- if (mCursor.isAfterLast()) return null;
-
- return mCursor.getString(
- mCursor.getColumnIndexOrThrow(UserDictionary.Words.SHORTCUT));
- }
-
- public static void deleteWord(final String word, final String shortcut,
- final ContentResolver resolver) {
- if (!IS_SHORTCUT_API_SUPPORTED) {
- resolver.delete(UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_SHORTCUT_UNSUPPORTED,
- new String[] { word });
- } else if (TextUtils.isEmpty(shortcut)) {
- resolver.delete(
- UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT,
- new String[] { word });
- } else {
- resolver.delete(
- UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT,
- new String[] { word, shortcut });
- }
- }
-
- private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer {
- private AlphabetIndexer mIndexer;
-
- private ViewBinder mViewBinder = new ViewBinder() {
-
- @Override
- public boolean setViewValue(final View v, final Cursor c, final int columnIndex) {
- if (!IS_SHORTCUT_API_SUPPORTED) {
- // just let SimpleCursorAdapter set the view values
- return false;
- }
- if (columnIndex == INDEX_SHORTCUT) {
- final String shortcut = c.getString(INDEX_SHORTCUT);
- if (TextUtils.isEmpty(shortcut)) {
- v.setVisibility(View.GONE);
- } else {
- ((TextView)v).setText(shortcut);
- v.setVisibility(View.VISIBLE);
- }
- v.invalidate();
- return true;
- }
-
- return false;
- }
- };
-
- public MyAdapter(final Context context, final int layout, final Cursor c,
- final String[] from, final int[] to) {
- super(context, layout, c, from, to, 0 /* flags */);
-
- if (null != c) {
- final String alphabet = context.getString(R.string.user_dict_fast_scroll_alphabet);
- final int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
- mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
- }
- setViewBinder(mViewBinder);
- }
-
- @Override
- public int getPositionForSection(final int section) {
- return null == mIndexer ? 0 : mIndexer.getPositionForSection(section);
- }
-
- @Override
- public int getSectionForPosition(final int position) {
- return null == mIndexer ? 0 : mIndexer.getSectionForPosition(position);
- }
-
- @Override
- public Object[] getSections() {
- return null == mIndexer ? null : mIndexer.getSections();
- }
- }
-}
-
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
deleted file mode 100644
index c0a946e42..000000000
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.userdictionary;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import java.util.Locale;
-
-/**
- * Utilities of the user dictionary settings
- * TODO: We really want to move these utilities to a static library.
- */
-public class UserDictionarySettingsUtils {
- public static String getLocaleDisplayName(Context context, String localeStr) {
- if (TextUtils.isEmpty(localeStr)) {
- // CAVEAT: localeStr should not be null because a null locale stands for the system
- // locale in UserDictionary.Words.addWord.
- return context.getResources().getString(R.string.user_dict_settings_all_languages);
- }
- final Locale locale = LocaleUtils.constructLocaleFromString(localeStr);
- final Locale systemLocale = context.getResources().getConfiguration().locale;
- return locale.getDisplayName(systemLocale);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
deleted file mode 100644
index 2aac7c57a..000000000
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
-
-import android.os.Build;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-public final class AdditionalSubtypeUtils {
- private static final String TAG = AdditionalSubtypeUtils.class.getSimpleName();
-
- private static final InputMethodSubtype[] EMPTY_SUBTYPE_ARRAY = new InputMethodSubtype[0];
-
- private AdditionalSubtypeUtils() {
- // This utility class is not publicly instantiable.
- }
-
- @UsedForTesting
- public static boolean isAdditionalSubtype(final InputMethodSubtype subtype) {
- return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE);
- }
-
- private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":";
- private static final int INDEX_OF_LOCALE = 0;
- private static final int INDEX_OF_KEYBOARD_LAYOUT = 1;
- private static final int INDEX_OF_EXTRA_VALUE = 2;
- private static final int LENGTH_WITHOUT_EXTRA_VALUE = (INDEX_OF_KEYBOARD_LAYOUT + 1);
- private static final int LENGTH_WITH_EXTRA_VALUE = (INDEX_OF_EXTRA_VALUE + 1);
- private static final String PREF_SUBTYPE_SEPARATOR = ";";
-
- private static InputMethodSubtype createAdditionalSubtypeInternal(
- final String localeString, final String keyboardLayoutSetName,
- final boolean isAsciiCapable, final boolean isEmojiCapable) {
- final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName);
- final String platformVersionDependentExtraValues = getPlatformVersionDependentExtraValue(
- localeString, keyboardLayoutSetName, isAsciiCapable, isEmojiCapable);
- final int platformVersionIndependentSubtypeId =
- getPlatformVersionIndependentSubtypeId(localeString, keyboardLayoutSetName);
- // NOTE: In KitKat and later, InputMethodSubtypeBuilder#setIsAsciiCapable is also available.
- // TODO: Use InputMethodSubtypeBuilder#setIsAsciiCapable when appropriate.
- return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId,
- R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE,
- platformVersionDependentExtraValues,
- false /* isAuxiliary */, false /* overrideImplicitlyEnabledSubtype */,
- platformVersionIndependentSubtypeId);
- }
-
- public static InputMethodSubtype createDummyAdditionalSubtype(
- final String localeString, final String keyboardLayoutSetName) {
- return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName,
- false /* isAsciiCapable */, false /* isEmojiCapable */);
- }
-
- public static InputMethodSubtype createAsciiEmojiCapableAdditionalSubtype(
- final String localeString, final String keyboardLayoutSetName) {
- return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName,
- true /* isAsciiCapable */, true /* isEmojiCapable */);
- }
-
- public static String getPrefSubtype(final InputMethodSubtype subtype) {
- final String localeString = subtype.getLocale();
- final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
- final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
- final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists(
- layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists(
- IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
- final String basePrefSubtype = localeString + LOCALE_AND_LAYOUT_SEPARATOR
- + keyboardLayoutSetName;
- return extraValue.isEmpty() ? basePrefSubtype
- : basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue;
- }
-
- public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) {
- if (TextUtils.isEmpty(prefSubtypes)) {
- return EMPTY_SUBTYPE_ARRAY;
- }
- final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR);
- final ArrayList<InputMethodSubtype> subtypesList = new ArrayList<>(prefSubtypeArray.length);
- for (final String prefSubtype : prefSubtypeArray) {
- final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
- if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE
- && elems.length != LENGTH_WITH_EXTRA_VALUE) {
- Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype + " in "
- + prefSubtypes);
- continue;
- }
- final String localeString = elems[INDEX_OF_LOCALE];
- final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT];
- // Here we assume that all the additional subtypes have AsciiCapable and EmojiCapable.
- // This is actually what the setting dialog for additional subtype is doing.
- final InputMethodSubtype subtype = createAsciiEmojiCapableAdditionalSubtype(
- localeString, keyboardLayoutSetName);
- if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) {
- // Skip unknown keyboard layout subtype. This may happen when predefined keyboard
- // layout has been removed.
- continue;
- }
- subtypesList.add(subtype);
- }
- return subtypesList.toArray(new InputMethodSubtype[subtypesList.size()]);
- }
-
- public static String createPrefSubtypes(final InputMethodSubtype[] subtypes) {
- if (subtypes == null || subtypes.length == 0) {
- return "";
- }
- final StringBuilder sb = new StringBuilder();
- for (final InputMethodSubtype subtype : subtypes) {
- if (sb.length() > 0) {
- sb.append(PREF_SUBTYPE_SEPARATOR);
- }
- sb.append(getPrefSubtype(subtype));
- }
- return sb.toString();
- }
-
- public static String createPrefSubtypes(final String[] prefSubtypes) {
- if (prefSubtypes == null || prefSubtypes.length == 0) {
- return "";
- }
- final StringBuilder sb = new StringBuilder();
- for (final String prefSubtype : prefSubtypes) {
- if (sb.length() > 0) {
- sb.append(PREF_SUBTYPE_SEPARATOR);
- }
- sb.append(prefSubtype);
- }
- return sb.toString();
- }
-
- /**
- * Returns the extra value that is optimized for the running OS.
- * <p>
- * Historically the extra value has been used as the last resort to annotate various kinds of
- * attributes. Some of these attributes are valid only on some platform versions. Thus we cannot
- * assume that the extra values stored in a persistent storage are always valid. We need to
- * regenerate the extra value on the fly instead.
- * </p>
- * @param localeString the locale string (e.g., "en_US").
- * @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak").
- * @param isAsciiCapable true when ASCII characters are supported with this layout.
- * @param isEmojiCapable true when Unicode Emoji characters are supported with this layout.
- * @return extra value that is optimized for the running OS.
- * @see #getPlatformVersionIndependentSubtypeId(String, String)
- */
- private static String getPlatformVersionDependentExtraValue(final String localeString,
- final String keyboardLayoutSetName, final boolean isAsciiCapable,
- final boolean isEmojiCapable) {
- final ArrayList<String> extraValueItems = new ArrayList<>();
- extraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
- if (isAsciiCapable) {
- extraValueItems.add(ASCII_CAPABLE);
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
- SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
- extraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
- SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName));
- }
- if (isEmojiCapable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- extraValueItems.add(EMOJI_CAPABLE);
- }
- extraValueItems.add(IS_ADDITIONAL_SUBTYPE);
- return TextUtils.join(",", extraValueItems);
- }
-
- /**
- * Returns the subtype ID that is supposed to be compatible between different version of OSes.
- * <p>
- * From the compatibility point of view, it is important to keep subtype id predictable and
- * stable between different OSes. For this purpose, the calculation code in this method is
- * carefully chosen and then fixed. Treat the following code as no more or less than a
- * hash function. Each component to be hashed can be different from the corresponding value
- * that is used to instantiate {@link InputMethodSubtype} actually.
- * For example, you don't need to update <code>compatibilityExtraValueItems</code> in this
- * method even when we need to add some new extra values for the actual instance of
- * {@link InputMethodSubtype}.
- * </p>
- * @param localeString the locale string (e.g., "en_US").
- * @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak").
- * @return a platform-version independent subtype ID.
- * @see #getPlatformVersionDependentExtraValue(String, String, boolean, boolean)
- */
- private static int getPlatformVersionIndependentSubtypeId(final String localeString,
- final String keyboardLayoutSetName) {
- // For compatibility reasons, we concatenate the extra values in the following order.
- // - KeyboardLayoutSet
- // - AsciiCapable
- // - UntranslatableReplacementStringInSubtypeName
- // - EmojiCapable
- // - isAdditionalSubtype
- final ArrayList<String> compatibilityExtraValueItems = new ArrayList<>();
- compatibilityExtraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
- compatibilityExtraValueItems.add(ASCII_CAPABLE);
- if (SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
- compatibilityExtraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
- SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName));
- }
- compatibilityExtraValueItems.add(EMOJI_CAPABLE);
- compatibilityExtraValueItems.add(IS_ADDITIONAL_SUBTYPE);
- final String compatibilityExtraValues = TextUtils.join(",", compatibilityExtraValueItems);
- return Arrays.hashCode(new Object[] {
- localeString,
- KEYBOARD_MODE,
- compatibilityExtraValues,
- false /* isAuxiliary */,
- false /* overrideImplicitlyEnabledSubtype */ });
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java
deleted file mode 100644
index 7a4150def..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.util.Log;
-
-public final class ApplicationUtils {
- private static final String TAG = ApplicationUtils.class.getSimpleName();
-
- private ApplicationUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static int getActivityTitleResId(final Context context,
- final Class<? extends Activity> cls) {
- final ComponentName cn = new ComponentName(context, cls);
- try {
- final ActivityInfo ai = context.getPackageManager().getActivityInfo(cn, 0);
- if (ai != null) {
- return ai.labelRes;
- }
- } catch (final NameNotFoundException e) {
- Log.e(TAG, "Failed to get settings activity title res id.", e);
- }
- return 0;
- }
-
- /**
- * A utility method to get the application's PackageInfo.versionName
- * @return the application's PackageInfo.versionName
- */
- public static String getVersionName(final Context context) {
- try {
- if (context == null) {
- return "";
- }
- final String packageName = context.getPackageName();
- final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
- return info.versionName;
- } catch (final NameNotFoundException e) {
- Log.e(TAG, "Could not find version info.", e);
- }
- return "";
- }
-
- /**
- * A utility method to get the application's PackageInfo.versionCode
- * @return the application's PackageInfo.versionCode
- */
- public static int getVersionCode(final Context context) {
- try {
- if (context == null) {
- return 0;
- }
- final String packageName = context.getPackageName();
- final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
- return info.versionCode;
- } catch (final NameNotFoundException e) {
- Log.e(TAG, "Could not find version info.", e);
- }
- return 0;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
deleted file mode 100644
index 1525f2d56..000000000
--- a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class is a holder of the result of an asynchronous computation.
- *
- * @param <E> the type of the result.
- */
-public class AsyncResultHolder<E> {
-
- private final Object mLock = new Object();
-
- private E mResult;
- private final String mTag;
- private final CountDownLatch mLatch;
-
- public AsyncResultHolder(final String tag) {
- mTag = tag;
- mLatch = new CountDownLatch(1);
- }
-
- /**
- * Sets the result value of this holder.
- *
- * @param result the value to set.
- */
- public void set(final E result) {
- synchronized(mLock) {
- if (mLatch.getCount() > 0) {
- mResult = result;
- mLatch.countDown();
- }
- }
- }
-
- /**
- * Gets the result value held in this holder.
- * Causes the current thread to wait unless the value is set or the specified time is elapsed.
- *
- * @param defaultValue the default value.
- * @param timeOut the maximum time to wait.
- * @return if the result is set before the time limit then the result, otherwise defaultValue.
- */
- public E get(final E defaultValue, final long timeOut) {
- try {
- return mLatch.await(timeOut, TimeUnit.MILLISECONDS) ? mResult : defaultValue;
- } catch (InterruptedException e) {
- Log.w(mTag, "get() : Interrupted after " + timeOut + " ms");
- return defaultValue;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
deleted file mode 100644
index c9ecade91..000000000
--- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.define.DebugFlags;
-
-public final class AutoCorrectionUtils {
- private static final boolean DBG = DebugFlags.DEBUG_ENABLED;
- private static final String TAG = AutoCorrectionUtils.class.getSimpleName();
-
- private AutoCorrectionUtils() {
- // Purely static class: can't instantiate.
- }
-
- public static boolean suggestionExceedsThreshold(final SuggestedWordInfo suggestion,
- final String consideredWord, final float threshold) {
- if (null != suggestion) {
- // Shortlist a whitelisted word
- if (suggestion.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
- return true;
- }
- // TODO: return suggestion.isAprapreateForAutoCorrection();
- if (!suggestion.isAprapreateForAutoCorrection()) {
- return false;
- }
- final int autoCorrectionSuggestionScore = suggestion.mScore;
- // TODO: when the normalized score of the first suggestion is nearly equals to
- // the normalized score of the second suggestion, behave less aggressive.
- final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore(
- consideredWord, suggestion.mWord, autoCorrectionSuggestionScore);
- if (DBG) {
- Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
- + autoCorrectionSuggestionScore + ", " + normalizedScore
- + "(" + threshold + ")");
- }
- if (normalizedScore >= threshold) {
- if (DBG) {
- Log.d(TAG, "Exceeds threshold.");
- }
- return true;
- }
- }
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
deleted file mode 100644
index 3bf9c6200..000000000
--- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class BinaryDictionaryUtils {
- private static final String TAG = BinaryDictionaryUtils.class.getSimpleName();
-
- private BinaryDictionaryUtils() {
- // This utility class is not publicly instantiable.
- }
-
- static {
- JniUtils.loadNativeLibrary();
- }
-
- @UsedForTesting
- private static native boolean createEmptyDictFileNative(String filePath, long dictVersion,
- String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray);
- private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
- private static native int setCurrentTimeForTestNative(int currentTime);
-
- public static DictionaryHeader getHeader(final File dictFile)
- throws IOException, UnsupportedFormatException {
- return getHeaderWithOffsetAndLength(dictFile, 0 /* offset */, dictFile.length());
- }
-
- public static DictionaryHeader getHeaderWithOffsetAndLength(final File dictFile,
- final long offset, final long length) throws IOException, UnsupportedFormatException {
- // dictType is never used for reading the header. Passing an empty string.
- final BinaryDictionary binaryDictionary = new BinaryDictionary(
- dictFile.getAbsolutePath(), offset, length,
- true /* useFullEditDistance */, null /* locale */, "" /* dictType */,
- false /* isUpdatable */);
- final DictionaryHeader header = binaryDictionary.getHeader();
- binaryDictionary.close();
- if (header == null) {
- throw new IOException();
- }
- return header;
- }
-
- public static boolean renameDict(final File dictFile, final File newDictFile) {
- if (dictFile.isFile()) {
- return dictFile.renameTo(newDictFile);
- } else if (dictFile.isDirectory()) {
- final String dictName = dictFile.getName();
- final String newDictName = newDictFile.getName();
- if (newDictFile.exists()) {
- return false;
- }
- for (final File file : dictFile.listFiles()) {
- if (!file.isFile()) {
- continue;
- }
- final String fileName = file.getName();
- final String newFileName = fileName.replaceFirst(
- Pattern.quote(dictName), Matcher.quoteReplacement(newDictName));
- if (!file.renameTo(new File(dictFile, newFileName))) {
- return false;
- }
- }
- return dictFile.renameTo(newDictFile);
- }
- return false;
- }
-
- @UsedForTesting
- public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
- final Locale locale, final Map<String, String> attributeMap) {
- final String[] keyArray = new String[attributeMap.size()];
- final String[] valueArray = new String[attributeMap.size()];
- int index = 0;
- for (final String key : attributeMap.keySet()) {
- keyArray[index] = key;
- valueArray[index] = attributeMap.get(key);
- index++;
- }
- return createEmptyDictFileNative(filePath, dictVersion, locale.toString(), keyArray,
- valueArray);
- }
-
- public static float calcNormalizedScore(final String before, final String after,
- final int score) {
- return calcNormalizedScoreNative(StringUtils.toCodePointArray(before),
- StringUtils.toCodePointArray(after), score);
- }
-
- /**
- * Control the current time to be used in the native code. If currentTime >= 0, this method sets
- * the current time and gets into test mode.
- * In test mode, set timestamp is used as the current time in the native code.
- * If currentTime < 0, quit the test mode and returns to using time() to get the current time.
- *
- * @param currentTime seconds since the unix epoch
- * @return current time got in the native code.
- */
- @UsedForTesting
- public static int setCurrentTimeForTest(final int currentTime) {
- return setCurrentTimeForTestNative(currentTime);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
deleted file mode 100644
index 47f65499d..000000000
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.text.InputType;
-import android.text.TextUtils;
-
-import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-
-import java.util.ArrayList;
-import java.util.Locale;
-
-public final class CapsModeUtils {
- private CapsModeUtils() {
- // This utility class is not publicly instantiable.
- }
-
- /**
- * Apply an auto-caps mode to a string.
- *
- * This intentionally does NOT apply manual caps mode. It only changes the capitalization if
- * the mode is one of the auto-caps modes.
- * @param s The string to capitalize.
- * @param capitalizeMode The mode in which to capitalize.
- * @param locale The locale for capitalizing.
- * @return The capitalized string.
- */
- public static String applyAutoCapsMode(final String s, final int capitalizeMode,
- final Locale locale) {
- if (WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == capitalizeMode) {
- return s.toUpperCase(locale);
- } else if (WordComposer.CAPS_MODE_AUTO_SHIFTED == capitalizeMode) {
- return StringUtils.capitalizeFirstCodePoint(s, locale);
- } else {
- return s;
- }
- }
-
- /**
- * Return whether a constant represents an auto-caps mode (either auto-shift or auto-shift-lock)
- * @param mode The mode to test for
- * @return true if this represents an auto-caps mode, false otherwise
- */
- public static boolean isAutoCapsMode(final int mode) {
- return WordComposer.CAPS_MODE_AUTO_SHIFTED == mode
- || WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode;
- }
-
- /**
- * Helper method to find out if a code point is starting punctuation.
- *
- * This include the Unicode START_PUNCTUATION category, but also some other symbols that are
- * starting, like the inverted question mark or the double quote.
- *
- * @param codePoint the code point
- * @return true if it's starting punctuation, false otherwise.
- */
- private static boolean isStartPunctuation(final int codePoint) {
- return (codePoint == Constants.CODE_DOUBLE_QUOTE || codePoint == Constants.CODE_SINGLE_QUOTE
- || codePoint == Constants.CODE_INVERTED_QUESTION_MARK
- || codePoint == Constants.CODE_INVERTED_EXCLAMATION_MARK
- || Character.getType(codePoint) == Character.START_PUNCTUATION);
- }
-
- /**
- * Determine what caps mode should be in effect at the current offset in
- * the text. Only the mode bits set in <var>reqModes</var> will be
- * checked. Note that the caps mode flags here are explicitly defined
- * to match those in {@link InputType}.
- *
- * This code is a straight copy of TextUtils.getCapsMode (modulo namespace and formatting
- * issues). This will change in the future as we simplify the code for our use and fix bugs.
- *
- * @param cs The text that should be checked for caps modes.
- * @param reqModes The modes to be checked: may be any combination of
- * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
- * {@link TextUtils#CAP_MODE_SENTENCES}.
- * @param spacingAndPunctuations The current spacing and punctuations settings.
- * @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs
- *
- * @return Returns the actual capitalization modes that can be in effect
- * at the current position, which is any combination of
- * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
- * {@link TextUtils#CAP_MODE_SENTENCES}.
- */
- public static int getCapsMode(final CharSequence cs, final int reqModes,
- final SpacingAndPunctuations spacingAndPunctuations, final boolean hasSpaceBefore) {
- // Quick description of what we want to do:
- // CAP_MODE_CHARACTERS is always on.
- // CAP_MODE_WORDS is on if there is some whitespace before the cursor.
- // CAP_MODE_SENTENCES is on if there is some whitespace before the cursor, and the end
- // of a sentence just before that.
- // We ignore opening parentheses and the like just before the cursor for purposes of
- // finding whitespace for WORDS and SENTENCES modes.
- // The end of a sentence ends with a period, question mark or exclamation mark. If it's
- // a period, it also needs not to be an abbreviation, which means it also needs to either
- // be immediately preceded by punctuation, or by a string of only letters with single
- // periods interleaved.
-
- // Step 1 : check for cap MODE_CHARACTERS. If it's looked for, it's always on.
- if ((reqModes & (TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES)) == 0) {
- // Here we are not looking for MODE_WORDS or MODE_SENTENCES, so since we already
- // evaluated MODE_CHARACTERS, we can return.
- return TextUtils.CAP_MODE_CHARACTERS & reqModes;
- }
-
- // Step 2 : Skip (ignore at the end of input) any opening punctuation. This includes
- // opening parentheses, brackets, opening quotes, everything that *opens* a span of
- // text in the linguistic sense. In RTL languages, this is still an opening sign, although
- // it may look like a right parenthesis for example. We also include double quote and
- // single quote since they aren't start punctuation in the unicode sense, but should still
- // be skipped for English. TODO: does this depend on the language?
- int i;
- if (hasSpaceBefore) {
- i = cs.length() + 1;
- } else {
- for (i = cs.length(); i > 0; i--) {
- final char c = cs.charAt(i - 1);
- if (!isStartPunctuation(c)) {
- break;
- }
- }
- }
-
- // We are now on the character that precedes any starting punctuation, so in the most
- // frequent case this will be whitespace or a letter, although it may occasionally be a
- // start of line, or some symbol.
-
- // Step 3 : Search for the start of a paragraph. From the starting point computed in step 2,
- // we go back over any space or tab char sitting there. We find the start of a paragraph
- // if the first char that's not a space or tab is a start of line (as in \n, start of text,
- // or some other similar characters).
- int j = i;
- char prevChar = Constants.CODE_SPACE;
- if (hasSpaceBefore) --j;
- while (j > 0) {
- prevChar = cs.charAt(j - 1);
- if (!Character.isSpaceChar(prevChar) && prevChar != Constants.CODE_TAB) break;
- j--;
- }
- if (j <= 0 || Character.isWhitespace(prevChar)) {
- if (spacingAndPunctuations.mUsesGermanRules) {
- // In German typography rules, there is a specific case that the first character
- // of a new line should not be capitalized if the previous line ends in a comma.
- boolean hasNewLine = false;
- while (--j >= 0 && Character.isWhitespace(prevChar)) {
- if (Constants.CODE_ENTER == prevChar) {
- hasNewLine = true;
- }
- prevChar = cs.charAt(j);
- }
- if (Constants.CODE_COMMA == prevChar && hasNewLine) {
- return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
- }
- }
- // There are only spacing chars between the start of the paragraph and the cursor,
- // defined as a isWhitespace() char that is neither a isSpaceChar() nor a tab. Both
- // MODE_WORDS and MODE_SENTENCES should be active.
- return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
- | TextUtils.CAP_MODE_SENTENCES) & reqModes;
- }
- if (i == j) {
- // If we don't have whitespace before index i, it means neither MODE_WORDS
- // nor mode sentences should be on so we can return right away.
- return TextUtils.CAP_MODE_CHARACTERS & reqModes;
- }
- if ((reqModes & TextUtils.CAP_MODE_SENTENCES) == 0) {
- // Here we know we have whitespace before the cursor (if not, we returned in the above
- // if i == j clause), so we need MODE_WORDS to be on. And we don't need to evaluate
- // MODE_SENTENCES so we can return right away.
- return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
- }
- // Please note that because of the reqModes & CAP_MODE_SENTENCES test a few lines above,
- // we know that MODE_SENTENCES is being requested.
-
- // Step 4 : Search for MODE_SENTENCES.
- // English is a special case in that "American typography" rules, which are the most common
- // in English, state that a sentence terminator immediately following a quotation mark
- // should be swapped with it and de-duplicated (included in the quotation mark),
- // e.g. <<Did they say, "let's go home?">>
- // No other language has such a rule as far as I know, instead putting inside the quotation
- // mark as the exact thing quoted and handling the surrounding punctuation independently,
- // e.g. <<Did they say, "let's go home"?>>
- if (spacingAndPunctuations.mUsesAmericanTypography) {
- for (; j > 0; j--) {
- // Here we look to go over any closing punctuation. This is because in dominant
- // variants of English, the final period is placed within double quotes and maybe
- // other closing punctuation signs. This is generally not true in other languages.
- final char c = cs.charAt(j - 1);
- if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
- && Character.getType(c) != Character.END_PUNCTUATION) {
- break;
- }
- }
- }
-
- if (j <= 0) return TextUtils.CAP_MODE_CHARACTERS & reqModes;
- char c = cs.charAt(--j);
-
- // We found the next interesting chunk of text ; next we need to determine if it's the
- // end of a sentence. If we have a sentence terminator (typically a question mark or an
- // exclamation mark), then it's the end of a sentence; however, we treat the abbreviation
- // marker specially because usually is the same char as the sentence separator (the
- // period in most languages) and in this case we need to apply a heuristic to determine
- // in which of these senses it's used.
- if (spacingAndPunctuations.isSentenceTerminator(c)
- && !spacingAndPunctuations.isAbbreviationMarker(c)) {
- return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
- | TextUtils.CAP_MODE_SENTENCES) & reqModes;
- }
- // If we reach here, we know we have whitespace before the cursor and before that there
- // is something that either does not terminate the sentence, or a symbol preceded by the
- // start of the text, or it's the sentence separator AND it happens to be the same code
- // point as the abbreviation marker.
- // If it's a symbol or something that does not terminate the sentence, then we need to
- // return caps for MODE_CHARACTERS and MODE_WORDS, but not for MODE_SENTENCES.
- if (!spacingAndPunctuations.isSentenceSeparator(c) || j <= 0) {
- return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
- }
-
- // We found out that we have a period. We need to determine if this is a full stop or
- // otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation
- // looks like (\w\.){2,}. Moreover, in German, you put periods after digits for dates
- // and some other things, and in German specifically we need to not go into autocaps after
- // a whitespace-digits-period sequence.
- // To find out, we will have a simple state machine with the following states :
- // START, WORD, PERIOD, ABBREVIATION, NUMBER
- // On START : (just before the first period)
- // letter => WORD
- // digit => NUMBER if German; end with caps otherwise
- // whitespace => end with no caps (it was a stand-alone period)
- // otherwise => end with caps (several periods/symbols in a row)
- // On WORD : (within the word just before the first period)
- // letter => WORD
- // period => PERIOD
- // otherwise => end with caps (it was a word with a full stop at the end)
- // On PERIOD : (period within a potential abbreviation)
- // letter => LETTER
- // otherwise => end with caps (it was not an abbreviation)
- // On LETTER : (letter within a potential abbreviation)
- // letter => LETTER
- // period => PERIOD
- // otherwise => end with no caps (it was an abbreviation)
- // On NUMBER : (period immediately preceded by one or more digits)
- // digit => NUMBER
- // letter => LETTER (promote to word)
- // otherwise => end with no caps (it was a whitespace-digits-period sequence,
- // or a punctuation-digits-period sequence like "11.11.")
- // "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This
- // should capitalize.
-
- final int START = 0;
- final int WORD = 1;
- final int PERIOD = 2;
- final int LETTER = 3;
- final int NUMBER = 4;
- final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
- | TextUtils.CAP_MODE_SENTENCES) & reqModes;
- final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
- int state = START;
- while (j > 0) {
- c = cs.charAt(--j);
- switch (state) {
- case START:
- if (Character.isLetter(c)) {
- state = WORD;
- } else if (Character.isWhitespace(c)) {
- return noCaps;
- } else if (Character.isDigit(c) && spacingAndPunctuations.mUsesGermanRules) {
- state = NUMBER;
- } else {
- return caps;
- }
- break;
- case WORD:
- if (Character.isLetter(c)) {
- state = WORD;
- } else if (spacingAndPunctuations.isSentenceSeparator(c)) {
- state = PERIOD;
- } else {
- return caps;
- }
- break;
- case PERIOD:
- if (Character.isLetter(c)) {
- state = LETTER;
- } else {
- return caps;
- }
- break;
- case LETTER:
- if (Character.isLetter(c)) {
- state = LETTER;
- } else if (spacingAndPunctuations.isSentenceSeparator(c)) {
- state = PERIOD;
- } else {
- return noCaps;
- }
- break;
- case NUMBER:
- if (Character.isLetter(c)) {
- state = WORD;
- } else if (Character.isDigit(c)) {
- state = NUMBER;
- } else {
- return noCaps;
- }
- }
- }
- // Here we arrived at the start of the line. This should behave exactly like whitespace.
- return (START == state || LETTER == state) ? noCaps : caps;
- }
-
- /**
- * Convert capitalize mode flags into human readable text.
- *
- * @param capsFlags The modes flags to be converted. It may be any combination of
- * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
- * {@link TextUtils#CAP_MODE_SENTENCES}.
- * @return the text that describe the <code>capsMode</code>.
- */
- public static String flagsToString(final int capsFlags) {
- final int capsFlagsMask = TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
- | TextUtils.CAP_MODE_SENTENCES;
- if ((capsFlags & ~capsFlagsMask) != 0) {
- return "unknown<0x" + Integer.toHexString(capsFlags) + ">";
- }
- final ArrayList<String> builder = new ArrayList<>();
- if ((capsFlags & android.text.TextUtils.CAP_MODE_CHARACTERS) != 0) {
- builder.add("characters");
- }
- if ((capsFlags & android.text.TextUtils.CAP_MODE_WORDS) != 0) {
- builder.add("words");
- }
- if ((capsFlags & android.text.TextUtils.CAP_MODE_SENTENCES) != 0) {
- builder.add("sentences");
- }
- return builder.isEmpty() ? "none" : TextUtils.join("|", builder);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
deleted file mode 100644
index 5c0c4328f..000000000
--- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.NgramProperty;
-import com.android.inputmethod.latin.makedict.ProbabilityInfo;
-import com.android.inputmethod.latin.makedict.WordProperty;
-
-import java.util.HashMap;
-
-public class CombinedFormatUtils {
- public static final String DICTIONARY_TAG = "dictionary";
- public static final String BIGRAM_TAG = "bigram";
- public static final String NGRAM_TAG = "ngram";
- public static final String NGRAM_PREV_WORD_TAG = "prev_word";
- public static final String PROBABILITY_TAG = "f";
- public static final String HISTORICAL_INFO_TAG = "historicalInfo";
- public static final String HISTORICAL_INFO_SEPARATOR = ":";
- public static final String WORD_TAG = "word";
- public static final String BEGINNING_OF_SENTENCE_TAG = "beginning_of_sentence";
- public static final String NOT_A_WORD_TAG = "not_a_word";
- public static final String POSSIBLY_OFFENSIVE_TAG = "possibly_offensive";
- public static final String TRUE_VALUE = "true";
-
- public static String formatAttributeMap(final HashMap<String, String> attributeMap) {
- final StringBuilder builder = new StringBuilder();
- builder.append(DICTIONARY_TAG + "=");
- if (attributeMap.containsKey(DictionaryHeader.DICTIONARY_ID_KEY)) {
- builder.append(attributeMap.get(DictionaryHeader.DICTIONARY_ID_KEY));
- }
- for (final String key : attributeMap.keySet()) {
- if (key.equals(DictionaryHeader.DICTIONARY_ID_KEY)) {
- continue;
- }
- final String value = attributeMap.get(key);
- builder.append("," + key + "=" + value);
- }
- builder.append("\n");
- return builder.toString();
- }
-
- public static String formatWordProperty(final WordProperty wordProperty) {
- final StringBuilder builder = new StringBuilder();
- builder.append(" " + WORD_TAG + "=" + wordProperty.mWord);
- builder.append(",");
- builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo));
- if (wordProperty.mIsBeginningOfSentence) {
- builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=" + TRUE_VALUE);
- }
- if (wordProperty.mIsNotAWord) {
- builder.append("," + NOT_A_WORD_TAG + "=" + TRUE_VALUE);
- }
- if (wordProperty.mIsPossiblyOffensive) {
- builder.append("," + POSSIBLY_OFFENSIVE_TAG + "=" + TRUE_VALUE);
- }
- builder.append("\n");
- if (wordProperty.mHasNgrams) {
- for (final NgramProperty ngramProperty : wordProperty.mNgrams) {
- builder.append(" " + NGRAM_TAG + "=" + ngramProperty.mTargetWord.mWord);
- builder.append(",");
- builder.append(formatProbabilityInfo(ngramProperty.mTargetWord.mProbabilityInfo));
- builder.append("\n");
- for (int i = 0; i < ngramProperty.mNgramContext.getPrevWordCount(); i++) {
- builder.append(" " + NGRAM_PREV_WORD_TAG + "[" + i + "]="
- + ngramProperty.mNgramContext.getNthPrevWord(i + 1));
- if (ngramProperty.mNgramContext.isNthPrevWordBeginningOfSentence(i + 1)) {
- builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true");
- }
- builder.append("\n");
- }
- }
- }
- return builder.toString();
- }
-
- public static String formatProbabilityInfo(final ProbabilityInfo probabilityInfo) {
- final StringBuilder builder = new StringBuilder();
- builder.append(PROBABILITY_TAG + "=" + probabilityInfo.mProbability);
- if (probabilityInfo.hasHistoricalInfo()) {
- builder.append(",");
- builder.append(HISTORICAL_INFO_TAG + "=");
- builder.append(probabilityInfo.mTimestamp);
- builder.append(HISTORICAL_INFO_SEPARATOR);
- builder.append(probabilityInfo.mLevel);
- builder.append(HISTORICAL_INFO_SEPARATOR);
- builder.append(probabilityInfo.mCount);
- }
- return builder.toString();
- }
-
- public static boolean isLiteralTrue(final String value) {
- return TRUE_VALUE.equalsIgnoreCase(value);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/CompletionInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CompletionInfoUtils.java
deleted file mode 100644
index 5ccf0e079..000000000
--- a/java/src/com/android/inputmethod/latin/utils/CompletionInfoUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.text.TextUtils;
-import android.view.inputmethod.CompletionInfo;
-
-import java.util.Arrays;
-
-/**
- * Utilities to do various stuff with CompletionInfo.
- */
-public class CompletionInfoUtils {
- private CompletionInfoUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static CompletionInfo[] removeNulls(final CompletionInfo[] src) {
- int j = 0;
- final CompletionInfo[] dst = new CompletionInfo[src.length];
- for (int i = 0; i < src.length; ++i) {
- if (null != src[i] && !TextUtils.isEmpty(src[i].getText())) {
- dst[j] = src[i];
- ++j;
- }
- }
- return Arrays.copyOfRange(dst, 0, j);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
deleted file mode 100644
index 41090c054..000000000
--- a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.annotation.TargetApi;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.inputmethodservice.ExtractEditText;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.text.Layout;
-import android.text.Spannable;
-import android.text.Spanned;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.widget.TextView;
-
-import com.android.inputmethod.compat.BuildCompatUtils;
-import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * This class allows input methods to extract {@link CursorAnchorInfo} directly from the given
- * {@link TextView}. This is useful and even necessary to support full-screen mode where the default
- * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} event callback must be
- * ignored because it reports the character locations of the target application rather than
- * characters on {@link ExtractEditText}.
- */
-public final class CursorAnchorInfoUtils {
- private CursorAnchorInfoUtils() {
- // This helper class is not instantiable.
- }
-
- private static boolean isPositionVisible(final View view, final float positionX,
- final float positionY) {
- final float[] position = new float[] { positionX, positionY };
- View currentView = view;
-
- while (currentView != null) {
- if (currentView != view) {
- // Local scroll is already taken into account in positionX/Y
- position[0] -= currentView.getScrollX();
- position[1] -= currentView.getScrollY();
- }
-
- if (position[0] < 0 || position[1] < 0 ||
- position[0] > currentView.getWidth() || position[1] > currentView.getHeight()) {
- return false;
- }
-
- if (!currentView.getMatrix().isIdentity()) {
- currentView.getMatrix().mapPoints(position);
- }
-
- position[0] += currentView.getLeft();
- position[1] += currentView.getTop();
-
- final ViewParent parent = currentView.getParent();
- if (parent instanceof View) {
- currentView = (View) parent;
- } else {
- // We've reached the ViewRoot, stop iterating
- currentView = null;
- }
- }
-
- // We've been able to walk up the view hierarchy and the position was never clipped
- return true;
- }
-
- /**
- * Extracts {@link CursorAnchorInfoCompatWrapper} from the given {@link TextView}.
- * @param textView the target text view from which {@link CursorAnchorInfoCompatWrapper} is to
- * be extracted.
- * @return the {@link CursorAnchorInfoCompatWrapper} object based on the current layout.
- * {@code null} if {@code Build.VERSION.SDK_INT} is 20 or prior or {@link TextView} is not
- * ready to provide layout information.
- */
- @Nullable
- public static CursorAnchorInfoCompatWrapper extractFromTextView(
- @Nonnull final TextView textView) {
- if (BuildCompatUtils.EFFECTIVE_SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- return null;
- }
- return CursorAnchorInfoCompatWrapper.wrap(extractFromTextViewInternal(textView));
- }
-
- /**
- * Returns {@link CursorAnchorInfo} from the given {@link TextView}.
- * @param textView the target text view from which {@link CursorAnchorInfo} is to be extracted.
- * @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it
- * is not feasible.
- */
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- @Nullable
- private static CursorAnchorInfo extractFromTextViewInternal(@Nonnull final TextView textView) {
- final Layout layout = textView.getLayout();
- if (layout == null) {
- return null;
- }
-
- final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
-
- final int selectionStart = textView.getSelectionStart();
- builder.setSelectionRange(selectionStart, textView.getSelectionEnd());
-
- // Construct transformation matrix from view local coordinates to screen coordinates.
- final Matrix viewToScreenMatrix = new Matrix(textView.getMatrix());
- final int[] viewOriginInScreen = new int[2];
- textView.getLocationOnScreen(viewOriginInScreen);
- viewToScreenMatrix.postTranslate(viewOriginInScreen[0], viewOriginInScreen[1]);
- builder.setMatrix(viewToScreenMatrix);
-
- if (layout.getLineCount() == 0) {
- return null;
- }
- final Rect lineBoundsWithoutOffset = new Rect();
- final Rect lineBoundsWithOffset = new Rect();
- layout.getLineBounds(0, lineBoundsWithoutOffset);
- textView.getLineBounds(0, lineBoundsWithOffset);
- final float viewportToContentHorizontalOffset = lineBoundsWithOffset.left
- - lineBoundsWithoutOffset.left - textView.getScrollX();
- final float viewportToContentVerticalOffset = lineBoundsWithOffset.top
- - lineBoundsWithoutOffset.top - textView.getScrollY();
-
- final CharSequence text = textView.getText();
- if (text instanceof Spannable) {
- // Here we assume that the composing text is marked as SPAN_COMPOSING flag. This is not
- // necessarily true, but basically works.
- int composingTextStart = text.length();
- int composingTextEnd = 0;
- final Spannable spannable = (Spannable) text;
- final Object[] spans = spannable.getSpans(0, text.length(), Object.class);
- for (Object span : spans) {
- final int spanFlag = spannable.getSpanFlags(span);
- if ((spanFlag & Spanned.SPAN_COMPOSING) != 0) {
- composingTextStart = Math.min(composingTextStart,
- spannable.getSpanStart(span));
- composingTextEnd = Math.max(composingTextEnd, spannable.getSpanEnd(span));
- }
- }
-
- final boolean hasComposingText =
- (0 <= composingTextStart) && (composingTextStart < composingTextEnd);
- if (hasComposingText) {
- final CharSequence composingText = text.subSequence(composingTextStart,
- composingTextEnd);
- builder.setComposingText(composingTextStart, composingText);
-
- final int minLine = layout.getLineForOffset(composingTextStart);
- final int maxLine = layout.getLineForOffset(composingTextEnd - 1);
- for (int line = minLine; line <= maxLine; ++line) {
- final int lineStart = layout.getLineStart(line);
- final int lineEnd = layout.getLineEnd(line);
- final int offsetStart = Math.max(lineStart, composingTextStart);
- final int offsetEnd = Math.min(lineEnd, composingTextEnd);
- final boolean ltrLine =
- layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
- final float[] widths = new float[offsetEnd - offsetStart];
- layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths);
- final float top = layout.getLineTop(line);
- final float bottom = layout.getLineBottom(line);
- for (int offset = offsetStart; offset < offsetEnd; ++offset) {
- final float charWidth = widths[offset - offsetStart];
- final boolean isRtl = layout.isRtlCharAt(offset);
- final float primary = layout.getPrimaryHorizontal(offset);
- final float secondary = layout.getSecondaryHorizontal(offset);
- // TODO: This doesn't work perfectly for text with custom styles and TAB
- // chars.
- final float left;
- final float right;
- if (ltrLine) {
- if (isRtl) {
- left = secondary - charWidth;
- right = secondary;
- } else {
- left = primary;
- right = primary + charWidth;
- }
- } else {
- if (!isRtl) {
- left = secondary;
- right = secondary + charWidth;
- } else {
- left = primary - charWidth;
- right = primary;
- }
- }
- // TODO: Check top-right and bottom-left as well.
- final float localLeft = left + viewportToContentHorizontalOffset;
- final float localRight = right + viewportToContentHorizontalOffset;
- final float localTop = top + viewportToContentVerticalOffset;
- final float localBottom = bottom + viewportToContentVerticalOffset;
- final boolean isTopLeftVisible = isPositionVisible(textView,
- localLeft, localTop);
- final boolean isBottomRightVisible =
- isPositionVisible(textView, localRight, localBottom);
- int characterBoundsFlags = 0;
- if (isTopLeftVisible || isBottomRightVisible) {
- characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
- }
- if (!isTopLeftVisible || !isBottomRightVisible) {
- characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
- }
- if (isRtl) {
- characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
- }
- // Here offset is the index in Java chars.
- builder.addCharacterBounds(offset, localLeft, localTop, localRight,
- localBottom, characterBoundsFlags);
- }
- }
- }
- }
-
- // Treat selectionStart as the insertion point.
- if (0 <= selectionStart) {
- final int offset = selectionStart;
- final int line = layout.getLineForOffset(offset);
- final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
- + viewportToContentHorizontalOffset;
- final float insertionMarkerTop = layout.getLineTop(line)
- + viewportToContentVerticalOffset;
- final float insertionMarkerBaseline = layout.getLineBaseline(line)
- + viewportToContentVerticalOffset;
- final float insertionMarkerBottom = layout.getLineBottom(line)
- + viewportToContentVerticalOffset;
- final boolean isTopVisible =
- isPositionVisible(textView, insertionMarkerX, insertionMarkerTop);
- final boolean isBottomVisible =
- isPositionVisible(textView, insertionMarkerX, insertionMarkerBottom);
- int insertionMarkerFlags = 0;
- if (isTopVisible || isBottomVisible) {
- insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
- }
- if (!isTopVisible || !isBottomVisible) {
- insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
- }
- if (layout.isRtlCharAt(offset)) {
- insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
- }
- builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
- insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
- }
- return builder.build();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
deleted file mode 100644
index 1ab834fc3..000000000
--- a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import com.android.inputmethod.latin.define.DebugFlags;
-
-/**
- * A class for logging and debugging utility methods.
- */
-public final class DebugLogUtils {
- private final static String TAG = DebugLogUtils.class.getSimpleName();
- private final static boolean sDBG = DebugFlags.DEBUG_ENABLED;
-
- /**
- * Calls .toString() on its non-null argument or returns "null"
- * @param o the object to convert to a string
- * @return the result of .toString() or null
- */
- public static String s(final Object o) {
- return null == o ? "null" : o.toString();
- }
-
- /**
- * Get the string representation of the current stack trace, for debugging purposes.
- * @return a readable, carriage-return-separated string for the current stack trace.
- */
- public static String getStackTrace() {
- return getStackTrace(Integer.MAX_VALUE - 1);
- }
-
- /**
- * Get the string representation of the current stack trace, for debugging purposes.
- * @param limit the maximum number of stack frames to be returned.
- * @return a readable, carriage-return-separated string for the current stack trace.
- */
- public static String getStackTrace(final int limit) {
- final StringBuilder sb = new StringBuilder();
- try {
- throw new RuntimeException();
- } catch (final RuntimeException e) {
- final StackTraceElement[] frames = e.getStackTrace();
- // Start at 1 because the first frame is here and we don't care about it
- for (int j = 1; j < frames.length && j < limit + 1; ++j) {
- sb.append(frames[j].toString() + "\n");
- }
- }
- return sb.toString();
- }
-
- /**
- * Get the stack trace contained in an exception as a human-readable string.
- * @param t the throwable
- * @return the human-readable stack trace
- */
- public static String getStackTrace(final Throwable t) {
- final StringBuilder sb = new StringBuilder();
- final StackTraceElement[] frames = t.getStackTrace();
- for (int j = 0; j < frames.length; ++j) {
- sb.append(frames[j].toString() + "\n");
- }
- return sb.toString();
- }
-
- /**
- * Helper log method to ease null-checks and adding spaces.
- *
- * This sends all arguments to the log, separated by spaces. Any null argument is converted
- * to the "null" string. It uses a very visible tag and log level for debugging purposes.
- *
- * @param args the stuff to send to the log
- */
- public static void l(final Object... args) {
- if (!sDBG) return;
- final StringBuilder sb = new StringBuilder();
- for (final Object o : args) {
- sb.append(s(o).toString());
- sb.append(" ");
- }
- Log.e(TAG, sb.toString());
- }
-
- /**
- * Helper log method to put stuff in red.
- *
- * This does the same as #l but prints in red
- *
- * @param args the stuff to send to the log
- */
- public static void r(final Object... args) {
- if (!sDBG) return;
- final StringBuilder sb = new StringBuilder("\u001B[31m");
- for (final Object o : args) {
- sb.append(s(o).toString());
- sb.append(" ");
- }
- sb.append("\u001B[0m");
- Log.e(TAG, sb.toString());
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/DialogUtils.java b/java/src/com/android/inputmethod/latin/utils/DialogUtils.java
deleted file mode 100644
index a05c932d0..000000000
--- a/java/src/com/android/inputmethod/latin/utils/DialogUtils.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-import android.view.ContextThemeWrapper;
-
-import com.android.inputmethod.latin.R;
-
-public final class DialogUtils {
- private DialogUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static Context getPlatformDialogThemeContext(final Context context) {
- // Because {@link AlertDialog.Builder.create()} doesn't honor the specified theme with
- // createThemeContextWrapper=false, the result dialog box has unneeded paddings around it.
- return new ContextThemeWrapper(context, R.style.platformDialogTheme);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryHeaderUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryHeaderUtils.java
deleted file mode 100644
index 5eb613c9b..000000000
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryHeaderUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.AssetFileAddress;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-
-import java.io.File;
-
-public class DictionaryHeaderUtils {
-
- public static int getContentVersion(AssetFileAddress fileAddress) {
- final DictionaryHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(
- new File(fileAddress.mFilename), fileAddress.mOffset, fileAddress.mLength);
- return Integer.parseInt(header.mVersionString);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
deleted file mode 100644
index cea2e13b1..000000000
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ /dev/null
@@ -1,613 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.dictionarypack.UpdateHandler;
-import com.android.inputmethod.latin.AssetFileAddress;
-import com.android.inputmethod.latin.BinaryDictionaryGetter;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.common.FileUtils;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * This class encapsulates the logic for the Latin-IME side of dictionary information management.
- */
-public class DictionaryInfoUtils {
- private static final String TAG = DictionaryInfoUtils.class.getSimpleName();
- public static final String RESOURCE_PACKAGE_NAME = R.class.getPackage().getName();
- private static final String DEFAULT_MAIN_DICT = "main";
- private static final String MAIN_DICT_PREFIX = "main_";
- private static final String DECODER_DICT_SUFFIX = DecoderSpecificConstants.DECODER_DICT_SUFFIX;
- // 6 digits - unicode is limited to 21 bits
- private static final int MAX_HEX_DIGITS_FOR_CODEPOINT = 6;
-
- private static final String TEMP_DICT_FILE_SUB = UpdateHandler.TEMP_DICT_FILE_SUB;
-
- public static class DictionaryInfo {
- private static final String LOCALE_COLUMN = "locale";
- private static final String WORDLISTID_COLUMN = "id";
- private static final String LOCAL_FILENAME_COLUMN = "filename";
- private static final String DESCRIPTION_COLUMN = "description";
- private static final String DATE_COLUMN = "date";
- private static final String FILESIZE_COLUMN = "filesize";
- private static final String VERSION_COLUMN = "version";
-
- @Nonnull public final String mId;
- @Nonnull public final Locale mLocale;
- @Nullable public final String mDescription;
- @Nullable public final String mFilename;
- public final long mFilesize;
- public final long mModifiedTimeMillis;
- public final int mVersion;
-
- public DictionaryInfo(@Nonnull String id, @Nonnull Locale locale,
- @Nullable String description, @Nullable String filename,
- long filesize, long modifiedTimeMillis, int version) {
- mId = id;
- mLocale = locale;
- mDescription = description;
- mFilename = filename;
- mFilesize = filesize;
- mModifiedTimeMillis = modifiedTimeMillis;
- mVersion = version;
- }
-
- public ContentValues toContentValues() {
- final ContentValues values = new ContentValues();
- values.put(WORDLISTID_COLUMN, mId);
- values.put(LOCALE_COLUMN, mLocale.toString());
- values.put(DESCRIPTION_COLUMN, mDescription);
- values.put(LOCAL_FILENAME_COLUMN, mFilename != null ? mFilename : "");
- values.put(DATE_COLUMN, TimeUnit.MILLISECONDS.toSeconds(mModifiedTimeMillis));
- values.put(FILESIZE_COLUMN, mFilesize);
- values.put(VERSION_COLUMN, mVersion);
- return values;
- }
-
- @Override
- public String toString() {
- return "DictionaryInfo : Id = '" + mId
- + "' : Locale=" + mLocale
- + " : Version=" + mVersion;
- }
- }
-
- private DictionaryInfoUtils() {
- // Private constructor to forbid instantation of this helper class.
- }
-
- /**
- * Returns whether we may want to use this character as part of a file name.
- *
- * This basically only accepts ascii letters and numbers, and rejects everything else.
- */
- private static boolean isFileNameCharacter(int codePoint) {
- if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit
- if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase
- if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase
- return codePoint == '_'; // Underscore
- }
-
- /**
- * Escapes a string for any characters that may be suspicious for a file or directory name.
- *
- * Concretely this does a sort of URL-encoding except it will encode everything that's not
- * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which
- * we cannot allow here)
- */
- // TODO: create a unit test for this method
- public static String replaceFileNameDangerousCharacters(final String name) {
- // This assumes '%' is fully available as a non-separator, normal
- // character in a file name. This is probably true for all file systems.
- final StringBuilder sb = new StringBuilder();
- final int nameLength = name.length();
- for (int i = 0; i < nameLength; i = name.offsetByCodePoints(i, 1)) {
- final int codePoint = name.codePointAt(i);
- if (DictionaryInfoUtils.isFileNameCharacter(codePoint)) {
- sb.appendCodePoint(codePoint);
- } else {
- sb.append(String.format((Locale)null, "%%%1$0" + MAX_HEX_DIGITS_FOR_CODEPOINT + "x",
- codePoint));
- }
- }
- return sb.toString();
- }
-
- /**
- * Helper method to get the top level cache directory.
- */
- private static String getWordListCacheDirectory(final Context context) {
- return context.getFilesDir() + File.separator + "dicts";
- }
-
- /**
- * Helper method to get the top level cache directory.
- */
- public static String getWordListStagingDirectory(final Context context) {
- return context.getFilesDir() + File.separator + "staging";
- }
-
- /**
- * Helper method to get the top level temp directory.
- */
- public static String getWordListTempDirectory(final Context context) {
- return context.getFilesDir() + File.separator + "tmp";
- }
-
- /**
- * Reverse escaping done by {@link #replaceFileNameDangerousCharacters(String)}.
- */
- @Nonnull
- public static String getWordListIdFromFileName(@Nonnull final String fname) {
- final StringBuilder sb = new StringBuilder();
- final int fnameLength = fname.length();
- for (int i = 0; i < fnameLength; i = fname.offsetByCodePoints(i, 1)) {
- final int codePoint = fname.codePointAt(i);
- if ('%' != codePoint) {
- sb.appendCodePoint(codePoint);
- } else {
- // + 1 to pass the % sign
- final int encodedCodePoint = Integer.parseInt(
- fname.substring(i + 1, i + 1 + MAX_HEX_DIGITS_FOR_CODEPOINT), 16);
- i += MAX_HEX_DIGITS_FOR_CODEPOINT;
- sb.appendCodePoint(encodedCodePoint);
- }
- }
- return sb.toString();
- }
-
- /**
- * Helper method to the list of cache directories, one for each distinct locale.
- */
- public static File[] getCachedDirectoryList(final Context context) {
- return new File(DictionaryInfoUtils.getWordListCacheDirectory(context)).listFiles();
- }
-
- public static File[] getStagingDirectoryList(final Context context) {
- return new File(DictionaryInfoUtils.getWordListStagingDirectory(context)).listFiles();
- }
-
- @Nullable
- public static File[] getUnusedDictionaryList(final Context context) {
- return context.getFilesDir().listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String filename) {
- return !TextUtils.isEmpty(filename) && filename.endsWith(".dict")
- && filename.contains(TEMP_DICT_FILE_SUB);
- }
- });
- }
-
- /**
- * Returns the category for a given file name.
- *
- * This parses the file name, extracts the category, and returns it. See
- * {@link #getMainDictId(Locale)} and {@link #isMainWordListId(String)}.
- * @return The category as a string or null if it can't be found in the file name.
- */
- @Nullable
- public static String getCategoryFromFileName(@Nonnull final String fileName) {
- final String id = getWordListIdFromFileName(fileName);
- final String[] idArray = id.split(BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR);
- // An id is supposed to be in format category:locale, so splitting on the separator
- // should yield a 2-elements array
- if (2 != idArray.length) {
- return null;
- }
- return idArray[0];
- }
-
- /**
- * Find out the cache directory associated with a specific locale.
- */
- public static String getCacheDirectoryForLocale(final String locale, final Context context) {
- final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale);
- final String absoluteDirectoryName = getWordListCacheDirectory(context) + File.separator
- + relativeDirectoryName;
- final File directory = new File(absoluteDirectoryName);
- if (!directory.exists()) {
- if (!directory.mkdirs()) {
- Log.e(TAG, "Could not create the directory for locale" + locale);
- }
- }
- return absoluteDirectoryName;
- }
-
- /**
- * Generates a file name for the id and locale passed as an argument.
- *
- * In the current implementation the file name returned will always be unique for
- * any id/locale pair, but please do not expect that the id can be the same for
- * different dictionaries with different locales. An id should be unique for any
- * dictionary.
- * The file name is pretty much an URL-encoded version of the id inside a directory
- * named like the locale, except it will also escape characters that look dangerous
- * to some file systems.
- * @param id the id of the dictionary for which to get a file name
- * @param locale the locale for which to get the file name as a string
- * @param context the context to use for getting the directory
- * @return the name of the file to be created
- */
- public static String getCacheFileName(String id, String locale, Context context) {
- final String fileName = replaceFileNameDangerousCharacters(id);
- return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
- }
-
- public static String getStagingFileName(String id, String locale, Context context) {
- final String stagingDirectory = getWordListStagingDirectory(context);
- // create the directory if it does not exist.
- final File directory = new File(stagingDirectory);
- if (!directory.exists()) {
- if (!directory.mkdirs()) {
- Log.e(TAG, "Could not create the staging directory.");
- }
- }
- // e.g. id="main:en_in", locale ="en_IN"
- final String fileName = replaceFileNameDangerousCharacters(
- locale + TEMP_DICT_FILE_SUB + id);
- return stagingDirectory + File.separator + fileName;
- }
-
- public static void moveStagingFilesIfExists(Context context) {
- final File[] stagingFiles = DictionaryInfoUtils.getStagingDirectoryList(context);
- if (stagingFiles != null && stagingFiles.length > 0) {
- for (final File stagingFile : stagingFiles) {
- final String fileName = stagingFile.getName();
- final int index = fileName.indexOf(TEMP_DICT_FILE_SUB);
- if (index == -1) {
- // This should never happen.
- Log.e(TAG, "Staging file does not have ___ substring.");
- continue;
- }
- final String[] localeAndFileId = fileName.split(TEMP_DICT_FILE_SUB);
- if (localeAndFileId.length != 2) {
- Log.e(TAG, String.format("malformed staging file %s. Deleting.",
- stagingFile.getAbsoluteFile()));
- stagingFile.delete();
- continue;
- }
-
- final String locale = localeAndFileId[0];
- // already escaped while moving to staging.
- final String fileId = localeAndFileId[1];
- final String cacheDirectoryForLocale = getCacheDirectoryForLocale(locale, context);
- final String cacheFilename = cacheDirectoryForLocale + File.separator + fileId;
- final File cacheFile = new File(cacheFilename);
- // move the staging file to cache file.
- if (!FileUtils.renameTo(stagingFile, cacheFile)) {
- Log.e(TAG, String.format("Failed to rename from %s to %s.",
- stagingFile.getAbsoluteFile(), cacheFile.getAbsoluteFile()));
- }
- }
- }
- }
-
- public static boolean isMainWordListId(final String id) {
- final String[] idArray = id.split(BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR);
- // An id is supposed to be in format category:locale, so splitting on the separator
- // should yield a 2-elements array
- if (2 != idArray.length) {
- return false;
- }
- return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
- }
-
- /**
- * Find out whether a dictionary is available for this locale.
- * @param context the context on which to check resources.
- * @param locale the locale to check for.
- * @return whether a (non-placeholder) dictionary is available or not.
- */
- public static boolean isDictionaryAvailable(final Context context, final Locale locale) {
- final Resources res = context.getResources();
- return 0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
- }
-
- /**
- * Helper method to return a dictionary res id for a locale, or 0 if none.
- * @param res resources for the app
- * @param locale dictionary locale
- * @return main dictionary resource id
- */
- public static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res,
- final Locale locale) {
- int resId;
- // Try to find main_language_country dictionary.
- if (!locale.getCountry().isEmpty()) {
- final String dictLanguageCountry = MAIN_DICT_PREFIX
- + locale.toString().toLowerCase(Locale.ROOT) + DECODER_DICT_SUFFIX;
- if ((resId = res.getIdentifier(
- dictLanguageCountry, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
- return resId;
- }
- }
-
- // Try to find main_language dictionary.
- final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage() + DECODER_DICT_SUFFIX;
- if ((resId = res.getIdentifier(dictLanguage, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
- return resId;
- }
-
- // Not found, return 0
- return 0;
- }
-
- /**
- * Returns a main dictionary resource id
- * @param res resources for the app
- * @param locale dictionary locale
- * @return main dictionary resource id
- */
- public static int getMainDictionaryResourceId(final Resources res, final Locale locale) {
- int resourceId = getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
- if (0 != resourceId) {
- return resourceId;
- }
- return res.getIdentifier(DEFAULT_MAIN_DICT + DecoderSpecificConstants.DECODER_DICT_SUFFIX,
- "raw", RESOURCE_PACKAGE_NAME);
- }
-
- /**
- * Returns the id associated with the main word list for a specified locale.
- *
- * Word lists stored in Android Keyboard's resources are referred to as the "main"
- * word lists. Since they can be updated like any other list, we need to assign a
- * unique ID to them. This ID is just the name of the language (locale-wise) they
- * are for, and this method returns this ID.
- */
- public static String getMainDictId(@Nonnull final Locale locale) {
- // This works because we don't include by default different dictionaries for
- // different countries. This actually needs to return the id that we would
- // like to use for word lists included in resources, and the following is okay.
- return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY +
- BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.toString().toLowerCase();
- }
-
- public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file,
- final long offset, final long length) {
- try {
- final DictionaryHeader header =
- BinaryDictionaryUtils.getHeaderWithOffsetAndLength(file, offset, length);
- return header;
- } catch (UnsupportedFormatException e) {
- return null;
- } catch (IOException e) {
- return null;
- }
- }
-
- /**
- * Returns information of the dictionary.
- *
- * @param fileAddress the asset dictionary file address.
- * @param locale Locale for this file.
- * @return information of the specified dictionary.
- */
- private static DictionaryInfo createDictionaryInfoFromFileAddress(
- @Nonnull final AssetFileAddress fileAddress, final Locale locale) {
- final String id = getMainDictId(locale);
- final int version = DictionaryHeaderUtils.getContentVersion(fileAddress);
- final String description = SubtypeLocaleUtils
- .getSubtypeLocaleDisplayName(locale.toString());
- // Do not store the filename on db as it will try to move the filename from db to the
- // cached directory. If the filename is already in cached directory, this is not
- // necessary.
- final String filenameToStoreOnDb = null;
- return new DictionaryInfo(id, locale, description, filenameToStoreOnDb,
- fileAddress.mLength, new File(fileAddress.mFilename).lastModified(), version);
- }
-
- /**
- * Returns the information of the dictionary for the given {@link AssetFileAddress}.
- * If the file is corrupted or a pre-fava file, then the file gets deleted and the null
- * value is returned.
- */
- @Nullable
- private static DictionaryInfo createDictionaryInfoForUnCachedFile(
- @Nonnull final AssetFileAddress fileAddress, final Locale locale) {
- final String id = getMainDictId(locale);
- final int version = DictionaryHeaderUtils.getContentVersion(fileAddress);
-
- if (version == -1) {
- // Purge the pre-fava/corrupted unused dictionaires.
- fileAddress.deleteUnderlyingFile();
- return null;
- }
-
- final String description = SubtypeLocaleUtils
- .getSubtypeLocaleDisplayName(locale.toString());
-
- final File unCachedFile = new File(fileAddress.mFilename);
- // Store just the filename and not the full path.
- final String filenameToStoreOnDb = unCachedFile.getName();
- return new DictionaryInfo(id, locale, description, filenameToStoreOnDb, fileAddress.mLength,
- unCachedFile.lastModified(), version);
- }
-
- /**
- * Returns dictionary information for the given locale.
- */
- private static DictionaryInfo createDictionaryInfoFromLocale(Locale locale) {
- final String id = getMainDictId(locale);
- final int version = -1;
- final String description = SubtypeLocaleUtils
- .getSubtypeLocaleDisplayName(locale.toString());
- return new DictionaryInfo(id, locale, description, null, 0L, 0L, version);
- }
-
- private static void addOrUpdateDictInfo(final ArrayList<DictionaryInfo> dictList,
- final DictionaryInfo newElement) {
- final Iterator<DictionaryInfo> iter = dictList.iterator();
- while (iter.hasNext()) {
- final DictionaryInfo thisDictInfo = iter.next();
- if (thisDictInfo.mLocale.equals(newElement.mLocale)) {
- if (newElement.mVersion <= thisDictInfo.mVersion) {
- return;
- }
- iter.remove();
- }
- }
- dictList.add(newElement);
- }
-
- public static ArrayList<DictionaryInfo> getCurrentDictionaryFileNameAndVersionInfo(
- final Context context) {
- final ArrayList<DictionaryInfo> dictList = new ArrayList<>();
-
- // Retrieve downloaded dictionaries from cached directories
- final File[] directoryList = getCachedDirectoryList(context);
- if (null != directoryList) {
- for (final File directory : directoryList) {
- final String localeString = getWordListIdFromFileName(directory.getName());
- final File[] dicts = BinaryDictionaryGetter.getCachedWordLists(
- localeString, context);
- for (final File dict : dicts) {
- final String wordListId = getWordListIdFromFileName(dict.getName());
- if (!DictionaryInfoUtils.isMainWordListId(wordListId)) {
- continue;
- }
- final Locale locale = LocaleUtils.constructLocaleFromString(localeString);
- final AssetFileAddress fileAddress = AssetFileAddress.makeFromFile(dict);
- final DictionaryInfo dictionaryInfo =
- createDictionaryInfoFromFileAddress(fileAddress, locale);
- // Protect against cases of a less-specific dictionary being found, like an
- // en dictionary being used for an en_US locale. In this case, the en dictionary
- // should be used for en_US but discounted for listing purposes.
- if (dictionaryInfo == null || !dictionaryInfo.mLocale.equals(locale)) {
- continue;
- }
- addOrUpdateDictInfo(dictList, dictionaryInfo);
- }
- }
- }
-
- // Retrieve downloaded dictionaries from the unused dictionaries.
- File[] unusedDictionaryList = getUnusedDictionaryList(context);
- if (unusedDictionaryList != null) {
- for (File dictionaryFile : unusedDictionaryList) {
- String fileName = dictionaryFile.getName();
- int index = fileName.indexOf(TEMP_DICT_FILE_SUB);
- if (index == -1) {
- continue;
- }
- String locale = fileName.substring(0, index);
- DictionaryInfo dictionaryInfo = createDictionaryInfoForUnCachedFile(
- AssetFileAddress.makeFromFile(dictionaryFile),
- LocaleUtils.constructLocaleFromString(locale));
- if (dictionaryInfo != null) {
- addOrUpdateDictInfo(dictList, dictionaryInfo);
- }
- }
- }
-
- // Retrieve files from assets
- final Resources resources = context.getResources();
- final AssetManager assets = resources.getAssets();
- for (final String localeString : assets.getLocales()) {
- final Locale locale = LocaleUtils.constructLocaleFromString(localeString);
- final int resourceId =
- DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale(
- context.getResources(), locale);
- if (0 == resourceId) {
- continue;
- }
- final AssetFileAddress fileAddress =
- BinaryDictionaryGetter.loadFallbackResource(context, resourceId);
- final DictionaryInfo dictionaryInfo = createDictionaryInfoFromFileAddress(fileAddress,
- locale);
- // Protect against cases of a less-specific dictionary being found, like an
- // en dictionary being used for an en_US locale. In this case, the en dictionary
- // should be used for en_US but discounted for listing purposes.
- // TODO: Remove dictionaryInfo == null when the static LMs have the headers.
- if (dictionaryInfo == null || !dictionaryInfo.mLocale.equals(locale)) {
- continue;
- }
- addOrUpdateDictInfo(dictList, dictionaryInfo);
- }
-
- // Generate the dictionary information from the enabled subtypes. This will not
- // overwrite the real records.
- RichInputMethodManager.init(context);
- List<InputMethodSubtype> enabledSubtypes = RichInputMethodManager
- .getInstance().getMyEnabledInputMethodSubtypeList(true);
- for (InputMethodSubtype subtype : enabledSubtypes) {
- Locale locale = LocaleUtils.constructLocaleFromString(subtype.getLocale());
- DictionaryInfo dictionaryInfo = createDictionaryInfoFromLocale(locale);
- addOrUpdateDictInfo(dictList, dictionaryInfo);
- }
-
- return dictList;
- }
-
- @UsedForTesting
- public static boolean looksValidForDictionaryInsertion(final CharSequence text,
- final SpacingAndPunctuations spacingAndPunctuations) {
- if (TextUtils.isEmpty(text)) {
- return false;
- }
- final int length = text.length();
- if (length > DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH) {
- return false;
- }
- int i = 0;
- int digitCount = 0;
- while (i < length) {
- final int codePoint = Character.codePointAt(text, i);
- final int charCount = Character.charCount(codePoint);
- i += charCount;
- if (Character.isDigit(codePoint)) {
- // Count digits: see below
- digitCount += charCount;
- continue;
- }
- if (!spacingAndPunctuations.isWordCodePoint(codePoint)) {
- return false;
- }
- }
- // We reject strings entirely comprised of digits to avoid using PIN codes or credit
- // card numbers. It would come in handy for word prediction though; a good example is
- // when writing one's address where the street number is usually quite discriminative,
- // as well as the postal code.
- return digitCount < length;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
deleted file mode 100644
index 8ce6eff92..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utilities to manage executors.
- */
-public class ExecutorUtils {
-
- private static final String TAG = "ExecutorUtils";
-
- public static final String KEYBOARD = "Keyboard";
- public static final String SPELLING = "Spelling";
-
- private static ScheduledExecutorService sKeyboardExecutorService = newExecutorService(KEYBOARD);
- private static ScheduledExecutorService sSpellingExecutorService = newExecutorService(SPELLING);
-
- private static ScheduledExecutorService newExecutorService(final String name) {
- return Executors.newSingleThreadScheduledExecutor(new ExecutorFactory(name));
- }
-
- private static class ExecutorFactory implements ThreadFactory {
- private final String mName;
-
- private ExecutorFactory(final String name) {
- mName = name;
- }
-
- @Override
- public Thread newThread(final Runnable runnable) {
- Thread thread = new Thread(runnable, TAG);
- thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread thread, Throwable ex) {
- Log.w(mName + "-" + runnable.getClass().getSimpleName(), ex);
- }
- });
- return thread;
- }
- }
-
- @UsedForTesting
- private static ScheduledExecutorService sExecutorServiceForTests;
-
- @UsedForTesting
- public static void setExecutorServiceForTests(
- final ScheduledExecutorService executorServiceForTests) {
- sExecutorServiceForTests = executorServiceForTests;
- }
-
- //
- // Public methods used to schedule a runnable for execution.
- //
-
- /**
- * @param name Executor's name.
- * @return scheduled executor service used to run background tasks
- */
- public static ScheduledExecutorService getBackgroundExecutor(final String name) {
- if (sExecutorServiceForTests != null) {
- return sExecutorServiceForTests;
- }
- switch (name) {
- case KEYBOARD:
- return sKeyboardExecutorService;
- case SPELLING:
- return sSpellingExecutorService;
- default:
- throw new IllegalArgumentException("Invalid executor: " + name);
- }
- }
-
- public static void killTasks(final String name) {
- final ScheduledExecutorService executorService = getBackgroundExecutor(name);
- executorService.shutdownNow();
- try {
- executorService.awaitTermination(5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Log.wtf(TAG, "Failed to shut down: " + name);
- }
- if (executorService == sExecutorServiceForTests) {
- // Don't do anything to the test service.
- return;
- }
- switch (name) {
- case KEYBOARD:
- sKeyboardExecutorService = newExecutorService(KEYBOARD);
- break;
- case SPELLING:
- sSpellingExecutorService = newExecutorService(SPELLING);
- break;
- default:
- throw new IllegalArgumentException("Invalid executor: " + name);
- }
- }
-
- @UsedForTesting
- public static Runnable chain(final Runnable... runnables) {
- return new RunnableChain(runnables);
- }
-
- @UsedForTesting
- public static class RunnableChain implements Runnable {
- private final Runnable[] mRunnables;
-
- private RunnableChain(final Runnable... runnables) {
- if (runnables == null || runnables.length == 0) {
- throw new IllegalArgumentException("Attempting to construct an empty chain");
- }
- mRunnables = runnables;
- }
-
- @UsedForTesting
- public Runnable[] getRunnables() {
- return mRunnables;
- }
-
- @Override
- public void run() {
- for (Runnable runnable : mRunnables) {
- if (Thread.interrupted()) {
- return;
- }
- runnable.run();
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/FeedbackUtils.java b/java/src/com/android/inputmethod/latin/utils/FeedbackUtils.java
deleted file mode 100644
index 67de8ba32..000000000
--- a/java/src/com/android/inputmethod/latin/utils/FeedbackUtils.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-import android.content.Intent;
-
-@SuppressWarnings("unused")
-public class FeedbackUtils {
- public static boolean isHelpAndFeedbackFormSupported() {
- return false;
- }
-
- public static void showHelpAndFeedbackForm(Context context) {
- }
-
- public static int getAboutKeyboardTitleResId() {
- return 0;
- }
-
- public static Intent getAboutKeyboardIntent(Context context) {
- return null;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/FileTransforms.java b/java/src/com/android/inputmethod/latin/utils/FileTransforms.java
deleted file mode 100644
index 9f4584ec9..000000000
--- a/java/src/com/android/inputmethod/latin/utils/FileTransforms.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.zip.GZIPInputStream;
-
-public final class FileTransforms {
- public static OutputStream getCryptedStream(OutputStream out) {
- // Crypt the stream.
- return out;
- }
-
- public static InputStream getDecryptedStream(InputStream in) {
- // Decrypt the stream.
- return in;
- }
-
- public static InputStream getUncompressedStream(InputStream in) throws IOException {
- return new GZIPInputStream(in);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
deleted file mode 100644
index 147e57b13..000000000
--- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.dictionarypack.DictionarySettingsFragment;
-import com.android.inputmethod.latin.about.AboutPreferences;
-import com.android.inputmethod.latin.settings.AccountsSettingsFragment;
-import com.android.inputmethod.latin.settings.AdvancedSettingsFragment;
-import com.android.inputmethod.latin.settings.AppearanceSettingsFragment;
-import com.android.inputmethod.latin.settings.CorrectionSettingsFragment;
-import com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment;
-import com.android.inputmethod.latin.settings.DebugSettingsFragment;
-import com.android.inputmethod.latin.settings.GestureSettingsFragment;
-import com.android.inputmethod.latin.settings.PreferencesSettingsFragment;
-import com.android.inputmethod.latin.settings.SettingsFragment;
-import com.android.inputmethod.latin.settings.ThemeSettingsFragment;
-import com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment;
-import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragment;
-import com.android.inputmethod.latin.userdictionary.UserDictionaryList;
-import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker;
-import com.android.inputmethod.latin.userdictionary.UserDictionarySettings;
-
-import java.util.HashSet;
-
-public class FragmentUtils {
- private static final HashSet<String> sLatinImeFragments = new HashSet<>();
- static {
- sLatinImeFragments.add(DictionarySettingsFragment.class.getName());
- sLatinImeFragments.add(AboutPreferences.class.getName());
- sLatinImeFragments.add(PreferencesSettingsFragment.class.getName());
- sLatinImeFragments.add(AccountsSettingsFragment.class.getName());
- sLatinImeFragments.add(AppearanceSettingsFragment.class.getName());
- sLatinImeFragments.add(ThemeSettingsFragment.class.getName());
- sLatinImeFragments.add(CustomInputStyleSettingsFragment.class.getName());
- sLatinImeFragments.add(GestureSettingsFragment.class.getName());
- sLatinImeFragments.add(CorrectionSettingsFragment.class.getName());
- sLatinImeFragments.add(AdvancedSettingsFragment.class.getName());
- sLatinImeFragments.add(DebugSettingsFragment.class.getName());
- sLatinImeFragments.add(SettingsFragment.class.getName());
- sLatinImeFragments.add(SpellCheckerSettingsFragment.class.getName());
- sLatinImeFragments.add(UserDictionaryAddWordFragment.class.getName());
- sLatinImeFragments.add(UserDictionaryList.class.getName());
- sLatinImeFragments.add(UserDictionaryLocalePicker.class.getName());
- sLatinImeFragments.add(UserDictionarySettings.class.getName());
- }
-
- public static boolean isValidFragment(String fragmentName) {
- return sLatinImeFragments.contains(fragmentName);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
deleted file mode 100644
index cea263b3b..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.permissions.PermissionsUtil;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-import java.util.concurrent.TimeUnit;
-
-public final class ImportantNoticeUtils {
- private static final String TAG = ImportantNoticeUtils.class.getSimpleName();
-
- // {@link SharedPreferences} name to save the last important notice version that has been
- // displayed to users.
- private static final String PREFERENCE_NAME = "important_notice_pref";
-
- private static final String KEY_SUGGEST_CONTACTS_NOTICE = "important_notice_suggest_contacts";
-
- @UsedForTesting
- static final String KEY_TIMESTAMP_OF_CONTACTS_NOTICE = "timestamp_of_suggest_contacts_notice";
-
- @UsedForTesting
- static final long TIMEOUT_OF_IMPORTANT_NOTICE = TimeUnit.HOURS.toMillis(23);
-
- // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key.
- // The value is zero until each multiuser completes system setup wizard.
- // Caveat: This is a hidden API.
- private static final String Settings_Secure_USER_SETUP_COMPLETE = "user_setup_complete";
- private static final int USER_SETUP_IS_NOT_COMPLETE = 0;
-
- private ImportantNoticeUtils() {
- // This utility class is not publicly instantiable.
- }
-
- @UsedForTesting
- static boolean isInSystemSetupWizard(final Context context) {
- try {
- final int userSetupComplete = Settings.Secure.getInt(
- context.getContentResolver(), Settings_Secure_USER_SETUP_COMPLETE);
- return userSetupComplete == USER_SETUP_IS_NOT_COMPLETE;
- } catch (final SettingNotFoundException e) {
- Log.w(TAG, "Can't find settings in Settings.Secure: key="
- + Settings_Secure_USER_SETUP_COMPLETE);
- return false;
- }
- }
-
- @UsedForTesting
- static SharedPreferences getImportantNoticePreferences(final Context context) {
- return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
- }
-
- @UsedForTesting
- static boolean hasContactsNoticeShown(final Context context) {
- return getImportantNoticePreferences(context).getBoolean(
- KEY_SUGGEST_CONTACTS_NOTICE, false);
- }
-
- public static boolean shouldShowImportantNotice(final Context context,
- final SettingsValues settingsValues) {
- // Check to see whether "Use Contacts" is enabled by the user.
- if (!settingsValues.mUseContactsDict) {
- return false;
- }
-
- if (hasContactsNoticeShown(context)) {
- return false;
- }
-
- // Don't show the dialog if we have all the permissions.
- if (PermissionsUtil.checkAllPermissionsGranted(
- context, Manifest.permission.READ_CONTACTS)) {
- return false;
- }
-
- final String importantNoticeTitle = getSuggestContactsNoticeTitle(context);
- if (TextUtils.isEmpty(importantNoticeTitle)) {
- return false;
- }
- if (isInSystemSetupWizard(context)) {
- return false;
- }
- if (hasContactsNoticeTimeoutPassed(context, System.currentTimeMillis())) {
- updateContactsNoticeShown(context);
- return false;
- }
- return true;
- }
-
- public static String getSuggestContactsNoticeTitle(final Context context) {
- return context.getResources().getString(R.string.important_notice_suggest_contact_names);
- }
-
- @UsedForTesting
- static boolean hasContactsNoticeTimeoutPassed(
- final Context context, final long currentTimeInMillis) {
- final SharedPreferences prefs = getImportantNoticePreferences(context);
- if (!prefs.contains(KEY_TIMESTAMP_OF_CONTACTS_NOTICE)) {
- prefs.edit()
- .putLong(KEY_TIMESTAMP_OF_CONTACTS_NOTICE, currentTimeInMillis)
- .apply();
- }
- final long firstDisplayTimeInMillis = prefs.getLong(
- KEY_TIMESTAMP_OF_CONTACTS_NOTICE, currentTimeInMillis);
- final long elapsedTime = currentTimeInMillis - firstDisplayTimeInMillis;
- return elapsedTime >= TIMEOUT_OF_IMPORTANT_NOTICE;
- }
-
- public static void updateContactsNoticeShown(final Context context) {
- getImportantNoticePreferences(context)
- .edit()
- .putBoolean(KEY_SUGGEST_CONTACTS_NOTICE, true)
- .remove(KEY_TIMESTAMP_OF_CONTACTS_NOTICE)
- .apply();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/InputTypeUtils.java b/java/src/com/android/inputmethod/latin/utils/InputTypeUtils.java
deleted file mode 100644
index 19cd34011..000000000
--- a/java/src/com/android/inputmethod/latin/utils/InputTypeUtils.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.text.InputType;
-import android.view.inputmethod.EditorInfo;
-
-public final class InputTypeUtils implements InputType {
- private static final int WEB_TEXT_PASSWORD_INPUT_TYPE =
- TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_PASSWORD;
- private static final int WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE =
- TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
- private static final int NUMBER_PASSWORD_INPUT_TYPE =
- TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD;
- private static final int TEXT_PASSWORD_INPUT_TYPE =
- TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
- private static final int TEXT_VISIBLE_PASSWORD_INPUT_TYPE =
- TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
- private static final int[] SUPPRESSING_AUTO_SPACES_FIELD_VARIATION = {
- InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
- InputType.TYPE_TEXT_VARIATION_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD };
- public static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1;
-
- private InputTypeUtils() {
- // This utility class is not publicly instantiable.
- }
-
- private static boolean isWebEditTextInputType(final int inputType) {
- return inputType == (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
- }
-
- private static boolean isWebPasswordInputType(final int inputType) {
- return WEB_TEXT_PASSWORD_INPUT_TYPE != 0
- && inputType == WEB_TEXT_PASSWORD_INPUT_TYPE;
- }
-
- private static boolean isWebEmailAddressInputType(final int inputType) {
- return WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE != 0
- && inputType == WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE;
- }
-
- private static boolean isNumberPasswordInputType(final int inputType) {
- return NUMBER_PASSWORD_INPUT_TYPE != 0
- && inputType == NUMBER_PASSWORD_INPUT_TYPE;
- }
-
- private static boolean isTextPasswordInputType(final int inputType) {
- return inputType == TEXT_PASSWORD_INPUT_TYPE;
- }
-
- private static boolean isWebEmailAddressVariation(int variation) {
- return variation == TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
- }
-
- public static boolean isEmailVariation(final int variation) {
- return variation == TYPE_TEXT_VARIATION_EMAIL_ADDRESS
- || isWebEmailAddressVariation(variation);
- }
-
- public static boolean isWebInputType(final int inputType) {
- final int maskedInputType =
- inputType & (TYPE_MASK_CLASS | TYPE_MASK_VARIATION);
- return isWebEditTextInputType(maskedInputType) || isWebPasswordInputType(maskedInputType)
- || isWebEmailAddressInputType(maskedInputType);
- }
-
- // Please refer to TextView.isPasswordInputType
- public static boolean isPasswordInputType(final int inputType) {
- final int maskedInputType =
- inputType & (TYPE_MASK_CLASS | TYPE_MASK_VARIATION);
- return isTextPasswordInputType(maskedInputType) || isWebPasswordInputType(maskedInputType)
- || isNumberPasswordInputType(maskedInputType);
- }
-
- // Please refer to TextView.isVisiblePasswordInputType
- public static boolean isVisiblePasswordInputType(final int inputType) {
- final int maskedInputType =
- inputType & (TYPE_MASK_CLASS | TYPE_MASK_VARIATION);
- return maskedInputType == TEXT_VISIBLE_PASSWORD_INPUT_TYPE;
- }
-
- public static boolean isAutoSpaceFriendlyType(final int inputType) {
- if (TYPE_CLASS_TEXT != (TYPE_MASK_CLASS & inputType)) return false;
- final int variation = TYPE_MASK_VARIATION & inputType;
- for (final int fieldVariation : SUPPRESSING_AUTO_SPACES_FIELD_VARIATION) {
- if (variation == fieldVariation) return false;
- }
- return true;
- }
-
- public static int getImeOptionsActionIdFromEditorInfo(final EditorInfo editorInfo) {
- if ((editorInfo.imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
- return EditorInfo.IME_ACTION_NONE;
- } else if (editorInfo.actionLabel != null) {
- return IME_ACTION_CUSTOM_LABEL;
- } else {
- // Note: this is different from editorInfo.actionId, hence "ImeOptionsActionId"
- return editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/IntentUtils.java b/java/src/com/android/inputmethod/latin/utils/IntentUtils.java
deleted file mode 100644
index ea0168117..000000000
--- a/java/src/com/android/inputmethod/latin/utils/IntentUtils.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Intent;
-import android.text.TextUtils;
-
-public final class IntentUtils {
- private static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
- // TODO: Can these be constants instead of literal String constants?
- private static final String INPUT_METHOD_SUBTYPE_SETTINGS =
- "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
-
- private IntentUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static Intent getInputLanguageSelectionIntent(final String inputMethodId,
- final int flagsForSubtypeSettings) {
- // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
- final String action = INPUT_METHOD_SUBTYPE_SETTINGS;
- final Intent intent = new Intent(action);
- if (!TextUtils.isEmpty(inputMethodId)) {
- intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
- }
- if (flagsForSubtypeSettings > 0) {
- intent.setFlags(flagsForSubtypeSettings);
- }
- return intent;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/JniUtils.java b/java/src/com/android/inputmethod/latin/utils/JniUtils.java
deleted file mode 100644
index e7fdafaeb..000000000
--- a/java/src/com/android/inputmethod/latin/utils/JniUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import com.android.inputmethod.latin.define.JniLibName;
-
-public final class JniUtils {
- private static final String TAG = JniUtils.class.getSimpleName();
-
- static {
- try {
- System.loadLibrary(JniLibName.JNI_LIB_NAME);
- } catch (UnsatisfiedLinkError ule) {
- Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule);
- }
- }
-
- private JniUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static void loadNativeLibrary() {
- // Ensures the static initializer is called
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
deleted file mode 100644
index 6dd8d9711..000000000
--- a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.JsonReader;
-import android.util.JsonWriter;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public final class JsonUtils {
- private static final String TAG = JsonUtils.class.getSimpleName();
-
- private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName();
- private static final String STRING_CLASS_NAME = String.class.getSimpleName();
-
- private static final String EMPTY_STRING = "";
-
- public static List<Object> jsonStrToList(final String s) {
- final ArrayList<Object> list = new ArrayList<>();
- final JsonReader reader = new JsonReader(new StringReader(s));
- try {
- reader.beginArray();
- while (reader.hasNext()) {
- reader.beginObject();
- while (reader.hasNext()) {
- final String name = reader.nextName();
- if (name.equals(INTEGER_CLASS_NAME)) {
- list.add(reader.nextInt());
- } else if (name.equals(STRING_CLASS_NAME)) {
- list.add(reader.nextString());
- } else {
- Log.w(TAG, "Invalid name: " + name);
- reader.skipValue();
- }
- }
- reader.endObject();
- }
- reader.endArray();
- return list;
- } catch (final IOException e) {
- } finally {
- close(reader);
- }
- return Collections.<Object>emptyList();
- }
-
- public static String listToJsonStr(final List<Object> list) {
- if (list == null || list.isEmpty()) {
- return EMPTY_STRING;
- }
- final StringWriter sw = new StringWriter();
- final JsonWriter writer = new JsonWriter(sw);
- try {
- writer.beginArray();
- for (final Object o : list) {
- writer.beginObject();
- if (o instanceof Integer) {
- writer.name(INTEGER_CLASS_NAME).value((Integer)o);
- } else if (o instanceof String) {
- writer.name(STRING_CLASS_NAME).value((String)o);
- }
- writer.endObject();
- }
- writer.endArray();
- return sw.toString();
- } catch (final IOException e) {
- } finally {
- close(writer);
- }
- return EMPTY_STRING;
- }
-
- private static void close(final Closeable closeable) {
- try {
- if (closeable != null) {
- closeable.close();
- }
- } catch (final IOException e) {
- // Ignore
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageOnSpacebarUtils.java b/java/src/com/android/inputmethod/latin/utils/LanguageOnSpacebarUtils.java
deleted file mode 100644
index a5a1ea921..000000000
--- a/java/src/com/android/inputmethod/latin/utils/LanguageOnSpacebarUtils.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.RichInputMethodSubtype;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-
-/**
- * This class determines that the language name on the spacebar should be displayed in what format.
- */
-public final class LanguageOnSpacebarUtils {
- public static final int FORMAT_TYPE_NONE = 0;
- public static final int FORMAT_TYPE_LANGUAGE_ONLY = 1;
- public static final int FORMAT_TYPE_FULL_LOCALE = 2;
-
- private static List<InputMethodSubtype> sEnabledSubtypes = Collections.emptyList();
- private static boolean sIsSystemLanguageSameAsInputLanguage;
-
- private LanguageOnSpacebarUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static int getLanguageOnSpacebarFormatType(
- @Nonnull final RichInputMethodSubtype subtype) {
- if (subtype.isNoLanguage()) {
- return FORMAT_TYPE_FULL_LOCALE;
- }
- // Only this subtype is enabled and equals to the system locale.
- if (sEnabledSubtypes.size() < 2 && sIsSystemLanguageSameAsInputLanguage) {
- return FORMAT_TYPE_NONE;
- }
- final Locale locale = subtype.getLocale();
- if (locale == null) {
- return FORMAT_TYPE_NONE;
- }
- final String keyboardLanguage = locale.getLanguage();
- final String keyboardLayout = subtype.getKeyboardLayoutSetName();
- int sameLanguageAndLayoutCount = 0;
- for (final InputMethodSubtype ims : sEnabledSubtypes) {
- final String language = SubtypeLocaleUtils.getSubtypeLocale(ims).getLanguage();
- if (keyboardLanguage.equals(language) && keyboardLayout.equals(
- SubtypeLocaleUtils.getKeyboardLayoutSetName(ims))) {
- sameLanguageAndLayoutCount++;
- }
- }
- // Display full locale name only when there are multiple subtypes that have the same
- // locale and keyboard layout. Otherwise displaying language name is enough.
- return sameLanguageAndLayoutCount > 1 ? FORMAT_TYPE_FULL_LOCALE
- : FORMAT_TYPE_LANGUAGE_ONLY;
- }
-
- public static void setEnabledSubtypes(@Nonnull final List<InputMethodSubtype> enabledSubtypes) {
- sEnabledSubtypes = enabledSubtypes;
- }
-
- public static void onSubtypeChanged(@Nonnull final RichInputMethodSubtype subtype,
- final boolean implicitlyEnabledSubtype, @Nonnull final Locale systemLocale) {
- final Locale newLocale = subtype.getLocale();
- if (systemLocale.equals(newLocale)) {
- sIsSystemLanguageSameAsInputLanguage = true;
- return;
- }
- if (!systemLocale.getLanguage().equals(newLocale.getLanguage())) {
- sIsSystemLanguageSameAsInputLanguage = false;
- return;
- }
- // If the subtype is enabled explicitly, the language name should be displayed even when
- // the keyboard language and the system language are equal.
- sIsSystemLanguageSameAsInputLanguage = implicitlyEnabledSubtype;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
deleted file mode 100644
index 9a5be99b3..000000000
--- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import java.lang.ref.WeakReference;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class LeakGuardHandlerWrapper<T> extends Handler {
- private final WeakReference<T> mOwnerInstanceRef;
-
- public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance) {
- this(ownerInstance, Looper.myLooper());
- }
-
- public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance, final Looper looper) {
- super(looper);
- mOwnerInstanceRef = new WeakReference<>(ownerInstance);
- }
-
- @Nullable
- public T getOwnerInstance() {
- return mOwnerInstanceRef.get();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java b/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java
deleted file mode 100644
index ef1872bf4..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-public class ManagedProfileUtils {
- private static ManagedProfileUtils INSTANCE = new ManagedProfileUtils();
- private static ManagedProfileUtils sTestInstance;
-
- private ManagedProfileUtils() {
- // This utility class is not publicly instantiable.
- }
-
- @UsedForTesting
- public static void setTestInstance(final ManagedProfileUtils testInstance) {
- sTestInstance = testInstance;
- }
-
- public static ManagedProfileUtils getInstance() {
- return sTestInstance == null ? INSTANCE : sTestInstance;
- }
-
- public boolean hasWorkProfile(final Context context) {
- return false;
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java b/java/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java
deleted file mode 100644
index 97fb17de3..000000000
--- a/java/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.R;
-
-import android.content.Context;
-
-/**
- * Helper class to get the metadata URI and the additional ID.
- */
-@SuppressWarnings("unused")
-public class MetadataFileUriGetter {
- private MetadataFileUriGetter() {
- // This helper class is not instantiable.
- }
-
- public static String getMetadataUri(final Context context) {
- return context.getString(R.string.dictionary_pack_metadata_uri);
- }
-
- public static String getMetadataAdditionalId(final Context context) {
- return "";
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java b/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java
deleted file mode 100644
index c05ffd693..000000000
--- a/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.NgramContext.WordInfo;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-
-import java.util.Arrays;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nonnull;
-
-public final class NgramContextUtils {
- private NgramContextUtils() {
- // Intentional empty constructor for utility class.
- }
-
- private static final Pattern NEWLINE_REGEX = Pattern.compile("[\\r\\n]+");
- private static final Pattern SPACE_REGEX = Pattern.compile("\\s+");
- // Get context information from nth word before the cursor. n = 1 retrieves the words
- // immediately before the cursor, n = 2 retrieves the words before that, and so on. This splits
- // on whitespace only.
- // Also, it won't return words that end in a separator (if the nth word before the cursor
- // ends in a separator, it returns information representing beginning-of-sentence).
- // Example (when Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM is 2):
- // (n = 1) "abc def|" -> abc, def
- // (n = 1) "abc def |" -> abc, def
- // (n = 1) "abc 'def|" -> empty, 'def
- // (n = 1) "abc def. |" -> beginning-of-sentence
- // (n = 1) "abc def . |" -> beginning-of-sentence
- // (n = 2) "abc def|" -> beginning-of-sentence, abc
- // (n = 2) "abc def |" -> beginning-of-sentence, abc
- // (n = 2) "abc 'def|" -> empty. The context is different from "abc def", but we cannot
- // represent this situation using NgramContext. See TODO in the method.
- // TODO: The next example's result should be "abc, def". This have to be fixed before we
- // retrieve the prior context of Beginning-of-Sentence.
- // (n = 2) "abc def. |" -> beginning-of-sentence, abc
- // (n = 2) "abc def . |" -> abc, def
- // (n = 2) "abc|" -> beginning-of-sentence
- // (n = 2) "abc |" -> beginning-of-sentence
- // (n = 2) "abc. def|" -> beginning-of-sentence
- @Nonnull
- public static NgramContext getNgramContextFromNthPreviousWord(final CharSequence prev,
- final SpacingAndPunctuations spacingAndPunctuations, final int n) {
- if (prev == null) return NgramContext.EMPTY_PREV_WORDS_INFO;
- final String[] lines = NEWLINE_REGEX.split(prev);
- if (lines.length == 0) {
- return new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO);
- }
- final String[] w = SPACE_REGEX.split(lines[lines.length - 1]);
- final WordInfo[] prevWordsInfo =
- new WordInfo[DecoderSpecificConstants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
- Arrays.fill(prevWordsInfo, WordInfo.EMPTY_WORD_INFO);
- for (int i = 0; i < prevWordsInfo.length; i++) {
- final int focusedWordIndex = w.length - n - i;
- // Referring to the word after the focused word.
- if ((focusedWordIndex + 1) >= 0 && (focusedWordIndex + 1) < w.length) {
- final String wordFollowingTheNthPrevWord = w[focusedWordIndex + 1];
- if (!wordFollowingTheNthPrevWord.isEmpty()) {
- final char firstChar = wordFollowingTheNthPrevWord.charAt(0);
- if (spacingAndPunctuations.isWordConnector(firstChar)) {
- // The word following the focused word is starting with a word connector.
- // TODO: Return meaningful context for this case.
- break;
- }
- }
- }
- // If we can't find (n + i) words, the context is beginning-of-sentence.
- if (focusedWordIndex < 0) {
- prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO;
- break;
- }
-
- final String focusedWord = w[focusedWordIndex];
- // If the word is empty, the context is beginning-of-sentence.
- final int length = focusedWord.length();
- if (length <= 0) {
- prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO;
- break;
- }
- // If the word ends in a sentence terminator, the context is beginning-of-sentence.
- final char lastChar = focusedWord.charAt(length - 1);
- if (spacingAndPunctuations.isSentenceTerminator(lastChar)) {
- prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO;
- break;
- }
- // If ends in a word separator or connector, the context is unclear.
- // TODO: Return meaningful context for this case.
- if (spacingAndPunctuations.isWordSeparator(lastChar)
- || spacingAndPunctuations.isWordConnector(lastChar)) {
- break;
- }
- prevWordsInfo[i] = new WordInfo(focusedWord);
- }
- return new NgramContext(prevWordsInfo);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
deleted file mode 100644
index 737e33228..000000000
--- a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.Locale;
-
-/**
- * The status of the current recapitalize process.
- */
-public class RecapitalizeStatus {
- public static final int NOT_A_RECAPITALIZE_MODE = -1;
- public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0;
- public static final int CAPS_MODE_ALL_LOWER = 1;
- public static final int CAPS_MODE_FIRST_WORD_UPPER = 2;
- public static final int CAPS_MODE_ALL_UPPER = 3;
- // When adding a new mode, don't forget to update the CAPS_MODE_LAST constant.
- public static final int CAPS_MODE_LAST = CAPS_MODE_ALL_UPPER;
-
- private static final int[] ROTATION_STYLE = {
- CAPS_MODE_ORIGINAL_MIXED_CASE,
- CAPS_MODE_ALL_LOWER,
- CAPS_MODE_FIRST_WORD_UPPER,
- CAPS_MODE_ALL_UPPER
- };
-
- private static final int getStringMode(final String string, final int[] sortedSeparators) {
- if (StringUtils.isIdenticalAfterUpcase(string)) {
- return CAPS_MODE_ALL_UPPER;
- } else if (StringUtils.isIdenticalAfterDowncase(string)) {
- return CAPS_MODE_ALL_LOWER;
- } else if (StringUtils.isIdenticalAfterCapitalizeEachWord(string, sortedSeparators)) {
- return CAPS_MODE_FIRST_WORD_UPPER;
- } else {
- return CAPS_MODE_ORIGINAL_MIXED_CASE;
- }
- }
-
- public static String modeToString(final int recapitalizeMode) {
- switch (recapitalizeMode) {
- case NOT_A_RECAPITALIZE_MODE: return "undefined";
- case CAPS_MODE_ORIGINAL_MIXED_CASE: return "mixedCase";
- case CAPS_MODE_ALL_LOWER: return "allLower";
- case CAPS_MODE_FIRST_WORD_UPPER: return "firstWordUpper";
- case CAPS_MODE_ALL_UPPER: return "allUpper";
- default: return "unknown<" + recapitalizeMode + ">";
- }
- }
-
- /**
- * We store the location of the cursor and the string that was there before the recapitalize
- * action was done, and the location of the cursor and the string that was there after.
- */
- private int mCursorStartBefore;
- private String mStringBefore;
- private int mCursorStartAfter;
- private int mCursorEndAfter;
- private int mRotationStyleCurrentIndex;
- private boolean mSkipOriginalMixedCaseMode;
- private Locale mLocale;
- private int[] mSortedSeparators;
- private String mStringAfter;
- private boolean mIsStarted;
- private boolean mIsEnabled = true;
-
- private static final int[] EMPTY_STORTED_SEPARATORS = {};
-
- public RecapitalizeStatus() {
- // By default, initialize with fake values that won't match any real recapitalize.
- start(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS);
- stop();
- }
-
- public void start(final int cursorStart, final int cursorEnd, final String string,
- final Locale locale, final int[] sortedSeparators) {
- if (!mIsEnabled) {
- return;
- }
- mCursorStartBefore = cursorStart;
- mStringBefore = string;
- mCursorStartAfter = cursorStart;
- mCursorEndAfter = cursorEnd;
- mStringAfter = string;
- final int initialMode = getStringMode(mStringBefore, sortedSeparators);
- mLocale = locale;
- mSortedSeparators = sortedSeparators;
- if (CAPS_MODE_ORIGINAL_MIXED_CASE == initialMode) {
- mRotationStyleCurrentIndex = 0;
- mSkipOriginalMixedCaseMode = false;
- } else {
- // Find the current mode in the array.
- int currentMode;
- for (currentMode = ROTATION_STYLE.length - 1; currentMode > 0; --currentMode) {
- if (ROTATION_STYLE[currentMode] == initialMode) {
- break;
- }
- }
- mRotationStyleCurrentIndex = currentMode;
- mSkipOriginalMixedCaseMode = true;
- }
- mIsStarted = true;
- }
-
- public void stop() {
- mIsStarted = false;
- }
-
- public boolean isStarted() {
- return mIsStarted;
- }
-
- public void enable() {
- mIsEnabled = true;
- }
-
- public void disable() {
- mIsEnabled = false;
- }
-
- public boolean mIsEnabled() {
- return mIsEnabled;
- }
-
- public boolean isSetAt(final int cursorStart, final int cursorEnd) {
- return cursorStart == mCursorStartAfter && cursorEnd == mCursorEndAfter;
- }
-
- /**
- * Rotate through the different possible capitalization modes.
- */
- public void rotate() {
- final String oldResult = mStringAfter;
- int count = 0; // Protection against infinite loop.
- do {
- mRotationStyleCurrentIndex = (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
- if (CAPS_MODE_ORIGINAL_MIXED_CASE == ROTATION_STYLE[mRotationStyleCurrentIndex]
- && mSkipOriginalMixedCaseMode) {
- mRotationStyleCurrentIndex =
- (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
- }
- ++count;
- switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) {
- case CAPS_MODE_ORIGINAL_MIXED_CASE:
- mStringAfter = mStringBefore;
- break;
- case CAPS_MODE_ALL_LOWER:
- mStringAfter = mStringBefore.toLowerCase(mLocale);
- break;
- case CAPS_MODE_FIRST_WORD_UPPER:
- mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSortedSeparators,
- mLocale);
- break;
- case CAPS_MODE_ALL_UPPER:
- mStringAfter = mStringBefore.toUpperCase(mLocale);
- break;
- default:
- mStringAfter = mStringBefore;
- }
- } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1);
- mCursorEndAfter = mCursorStartAfter + mStringAfter.length();
- }
-
- /**
- * Remove leading/trailing whitespace from the considered string.
- */
- public void trim() {
- final int len = mStringBefore.length();
- int nonWhitespaceStart = 0;
- for (; nonWhitespaceStart < len;
- nonWhitespaceStart = mStringBefore.offsetByCodePoints(nonWhitespaceStart, 1)) {
- final int codePoint = mStringBefore.codePointAt(nonWhitespaceStart);
- if (!Character.isWhitespace(codePoint)) break;
- }
- int nonWhitespaceEnd = len;
- for (; nonWhitespaceEnd > 0;
- nonWhitespaceEnd = mStringBefore.offsetByCodePoints(nonWhitespaceEnd, -1)) {
- final int codePoint = mStringBefore.codePointBefore(nonWhitespaceEnd);
- if (!Character.isWhitespace(codePoint)) break;
- }
- // If nonWhitespaceStart >= nonWhitespaceEnd, that means the selection contained only
- // whitespace, so we leave it as is.
- if ((0 != nonWhitespaceStart || len != nonWhitespaceEnd)
- && nonWhitespaceStart < nonWhitespaceEnd) {
- mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd;
- mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart;
- mStringAfter = mStringBefore =
- mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd);
- }
- }
-
- public String getRecapitalizedString() {
- return mStringAfter;
- }
-
- public int getNewCursorStart() {
- return mCursorStartAfter;
- }
-
- public int getNewCursorEnd() {
- return mCursorEndAfter;
- }
-
- public int getCurrentMode() {
- return ROTATION_STYLE[mRotationStyleCurrentIndex];
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
deleted file mode 100644
index 37b34751a..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Insets;
-import android.os.Build;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.regex.PatternSyntaxException;
-
-public final class ResourceUtils {
- private static final String TAG = ResourceUtils.class.getSimpleName();
-
- public static final float UNDEFINED_RATIO = -1.0f;
- public static final int UNDEFINED_DIMENSION = -1;
-
- private ResourceUtils() {
- // This utility class is not publicly instantiable.
- }
-
- private static final HashMap<String, String> sDeviceOverrideValueMap = new HashMap<>();
-
- private static final String[] BUILD_KEYS_AND_VALUES = {
- "HARDWARE", Build.HARDWARE,
- "MODEL", Build.MODEL,
- "BRAND", Build.BRAND,
- "MANUFACTURER", Build.MANUFACTURER
- };
- private static final HashMap<String, String> sBuildKeyValues;
- private static final String sBuildKeyValuesDebugString;
-
- static {
- sBuildKeyValues = new HashMap<>();
- final ArrayList<String> keyValuePairs = new ArrayList<>();
- final int keyCount = BUILD_KEYS_AND_VALUES.length / 2;
- for (int i = 0; i < keyCount; i++) {
- final int index = i * 2;
- final String key = BUILD_KEYS_AND_VALUES[index];
- final String value = BUILD_KEYS_AND_VALUES[index + 1];
- sBuildKeyValues.put(key, value);
- keyValuePairs.add(key + '=' + value);
- }
- sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]";
- }
-
- public static String getDeviceOverrideValue(final Resources res, final int overrideResId,
- final String defaultValue) {
- final int orientation = res.getConfiguration().orientation;
- final String key = overrideResId + "-" + orientation;
- if (sDeviceOverrideValueMap.containsKey(key)) {
- return sDeviceOverrideValueMap.get(key);
- }
-
- final String[] overrideArray = res.getStringArray(overrideResId);
- final String overrideValue = findConstantForKeyValuePairs(sBuildKeyValues, overrideArray);
- // The overrideValue might be an empty string.
- if (overrideValue != null) {
- Log.i(TAG, "Find override value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " build=" + sBuildKeyValuesDebugString
- + " override=" + overrideValue);
- sDeviceOverrideValueMap.put(key, overrideValue);
- return overrideValue;
- }
-
- sDeviceOverrideValueMap.put(key, defaultValue);
- return defaultValue;
- }
-
- @SuppressWarnings("serial")
- static class DeviceOverridePatternSyntaxError extends Exception {
- public DeviceOverridePatternSyntaxError(final String message, final String expression) {
- this(message, expression, null);
- }
-
- public DeviceOverridePatternSyntaxError(final String message, final String expression,
- final Throwable throwable) {
- super(message + ": " + expression, throwable);
- }
- }
-
- /**
- * Find the condition that fulfills specified key value pairs from an array of
- * "condition,constant", and return the corresponding string constant. A condition is
- * "pattern1[:pattern2...] (or an empty string for the default). A pattern is
- * "key=regexp_value" string. The condition matches only if all patterns of the condition
- * are true for the specified key value pairs.
- *
- * For example, "condition,constant" has the following format.
- * - HARDWARE=mako,constantForNexus4
- * - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4
- * - ,defaultConstant
- *
- * @param keyValuePairs attributes to be used to look for a matched condition.
- * @param conditionConstantArray an array of "condition,constant" elements to be searched.
- * @return the constant part of the matched "condition,constant" element. Returns null if no
- * condition matches.
- * @see com.android.inputmethod.latin.utils.ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp()
- */
- @UsedForTesting
- static String findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs,
- final String[] conditionConstantArray) {
- if (conditionConstantArray == null || keyValuePairs == null) {
- return null;
- }
- String foundValue = null;
- for (final String conditionConstant : conditionConstantArray) {
- final int posComma = conditionConstant.indexOf(',');
- if (posComma < 0) {
- Log.w(TAG, "Array element has no comma: " + conditionConstant);
- continue;
- }
- final String condition = conditionConstant.substring(0, posComma);
- if (condition.isEmpty()) {
- Log.w(TAG, "Array element has no condition: " + conditionConstant);
- continue;
- }
- try {
- if (fulfillsCondition(keyValuePairs, condition)) {
- // Take first match
- if (foundValue == null) {
- foundValue = conditionConstant.substring(posComma + 1);
- }
- // And continue walking through all conditions.
- }
- } catch (final DeviceOverridePatternSyntaxError e) {
- Log.w(TAG, "Syntax error, ignored", e);
- }
- }
- return foundValue;
- }
-
- private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs,
- final String condition) throws DeviceOverridePatternSyntaxError {
- final String[] patterns = condition.split(":");
- // Check all patterns in a condition are true
- boolean matchedAll = true;
- for (final String pattern : patterns) {
- final int posEqual = pattern.indexOf('=');
- if (posEqual < 0) {
- throw new DeviceOverridePatternSyntaxError("Pattern has no '='", condition);
- }
- final String key = pattern.substring(0, posEqual);
- final String value = keyValuePairs.get(key);
- if (value == null) {
- throw new DeviceOverridePatternSyntaxError("Unknown key", condition);
- }
- final String patternRegexpValue = pattern.substring(posEqual + 1);
- try {
- if (!value.matches(patternRegexpValue)) {
- matchedAll = false;
- // And continue walking through all patterns.
- }
- } catch (final PatternSyntaxException e) {
- throw new DeviceOverridePatternSyntaxError("Syntax error", condition, e);
- }
- }
- return matchedAll;
- }
-
- public static int getDefaultKeyboardWidth(final Context context) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- // Since Android 15’s edge-to-edge enforcement, window insets should be considered.
- final WindowManager wm = context.getSystemService(WindowManager.class);
- final WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
- final Insets insets =
- windowMetrics
- .getWindowInsets()
- .getInsetsIgnoringVisibility(
- WindowInsets.Type.systemBars()
- | WindowInsets.Type.displayCutout());
- return windowMetrics.getBounds().width() - insets.left - insets.right;
- }
- final DisplayMetrics dm = context.getResources().getDisplayMetrics();
- return dm.widthPixels;
- }
-
- public static int getKeyboardHeight(final Resources res, final SettingsValues settingsValues) {
- final int defaultKeyboardHeight = getDefaultKeyboardHeight(res);
- if (settingsValues.mHasKeyboardResize) {
- // mKeyboardHeightScale Ranges from [.5,1.2], from xml/prefs_screen_debug.xml
- return (int)(defaultKeyboardHeight * settingsValues.mKeyboardHeightScale);
- }
- return defaultKeyboardHeight;
- }
-
- public static int getDefaultKeyboardHeight(final Resources res) {
- final DisplayMetrics dm = res.getDisplayMetrics();
- final String keyboardHeightInDp = getDeviceOverrideValue(
- res, R.array.keyboard_heights, null /* defaultValue */);
- final float keyboardHeight;
- if (TextUtils.isEmpty(keyboardHeightInDp)) {
- keyboardHeight = res.getDimension(R.dimen.config_default_keyboard_height);
- } else {
- keyboardHeight = Float.parseFloat(keyboardHeightInDp) * dm.density;
- }
- final float maxKeyboardHeight = res.getFraction(
- R.fraction.config_max_keyboard_height, dm.heightPixels, dm.heightPixels);
- float minKeyboardHeight = res.getFraction(
- R.fraction.config_min_keyboard_height, dm.heightPixels, dm.heightPixels);
- if (minKeyboardHeight < 0.0f) {
- // Specified fraction was negative, so it should be calculated against display
- // width.
- minKeyboardHeight = -res.getFraction(
- R.fraction.config_min_keyboard_height, dm.widthPixels, dm.widthPixels);
- }
- // Keyboard height will not exceed maxKeyboardHeight and will not be less than
- // minKeyboardHeight.
- return (int)Math.max(Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
- }
-
- public static boolean isValidFraction(final float fraction) {
- return fraction >= 0.0f;
- }
-
- // {@link Resources#getDimensionPixelSize(int)} returns at least one pixel size.
- public static boolean isValidDimensionPixelSize(final int dimension) {
- return dimension > 0;
- }
-
- // {@link Resources#getDimensionPixelOffset(int)} may return zero pixel offset.
- public static boolean isValidDimensionPixelOffset(final int dimension) {
- return dimension >= 0;
- }
-
- public static float getFloatFromFraction(final Resources res, final int fractionResId) {
- return res.getFraction(fractionResId, 1, 1);
- }
-
- public static float getFraction(final TypedArray a, final int index, final float defValue) {
- final TypedValue value = a.peekValue(index);
- if (value == null || !isFractionValue(value)) {
- return defValue;
- }
- return a.getFraction(index, 1, 1, defValue);
- }
-
- public static float getFraction(final TypedArray a, final int index) {
- return getFraction(a, index, UNDEFINED_RATIO);
- }
-
- public static int getDimensionPixelSize(final TypedArray a, final int index) {
- final TypedValue value = a.peekValue(index);
- if (value == null || !isDimensionValue(value)) {
- return ResourceUtils.UNDEFINED_DIMENSION;
- }
- return a.getDimensionPixelSize(index, ResourceUtils.UNDEFINED_DIMENSION);
- }
-
- public static float getDimensionOrFraction(final TypedArray a, final int index, final int base,
- final float defValue) {
- final TypedValue value = a.peekValue(index);
- if (value == null) {
- return defValue;
- }
- if (isFractionValue(value)) {
- return a.getFraction(index, base, base, defValue);
- } else if (isDimensionValue(value)) {
- return a.getDimension(index, defValue);
- }
- return defValue;
- }
-
- public static int getEnumValue(final TypedArray a, final int index, final int defValue) {
- final TypedValue value = a.peekValue(index);
- if (value == null) {
- return defValue;
- }
- if (isIntegerValue(value)) {
- return a.getInt(index, defValue);
- }
- return defValue;
- }
-
- public static boolean isFractionValue(final TypedValue v) {
- return v.type == TypedValue.TYPE_FRACTION;
- }
-
- public static boolean isDimensionValue(final TypedValue v) {
- return v.type == TypedValue.TYPE_DIMENSION;
- }
-
- public static boolean isIntegerValue(final TypedValue v) {
- return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
- }
-
- public static boolean isStringValue(final TypedValue v) {
- return v.type == TypedValue.TYPE_STRING;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/RunInLocale.java b/java/src/com/android/inputmethod/latin/utils/RunInLocale.java
deleted file mode 100644
index 1ea16e6ef..000000000
--- a/java/src/com/android/inputmethod/latin/utils/RunInLocale.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.res.Configuration;
-import android.content.res.Resources;
-
-import java.util.Locale;
-
-public abstract class RunInLocale<T> {
- private static final Object sLockForRunInLocale = new Object();
-
- protected abstract T job(final Resources res);
-
- /**
- * Execute {@link #job(Resources)} method in specified system locale exclusively.
- *
- * @param res the resources to use.
- * @param newLocale the locale to change to. Run in system locale if null.
- * @return the value returned from {@link #job(Resources)}.
- */
- public T runInLocale(final Resources res, final Locale newLocale) {
- synchronized (sLockForRunInLocale) {
- final Configuration conf = res.getConfiguration();
- if (newLocale == null || newLocale.equals(conf.locale)) {
- return job(res);
- }
- final Locale savedLocale = conf.locale;
- try {
- conf.locale = newLocale;
- res.updateConfiguration(conf, null);
- return job(res);
- } finally {
- conf.locale = savedLocale;
- res.updateConfiguration(conf, null);
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
deleted file mode 100644
index 537713091..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import java.util.Locale;
-import java.util.TreeMap;
-
-/**
- * A class to help with handling different writing scripts.
- */
-public class ScriptUtils {
-
- // Used for hardware keyboards
- public static final int SCRIPT_UNKNOWN = -1;
-
- public static final int SCRIPT_ARABIC = 0;
- public static final int SCRIPT_ARMENIAN = 1;
- public static final int SCRIPT_BENGALI = 2;
- public static final int SCRIPT_CYRILLIC = 3;
- public static final int SCRIPT_DEVANAGARI = 4;
- public static final int SCRIPT_GEORGIAN = 5;
- public static final int SCRIPT_GREEK = 6;
- public static final int SCRIPT_HEBREW = 7;
- public static final int SCRIPT_KANNADA = 8;
- public static final int SCRIPT_KHMER = 9;
- public static final int SCRIPT_LAO = 10;
- public static final int SCRIPT_LATIN = 11;
- public static final int SCRIPT_MALAYALAM = 12;
- public static final int SCRIPT_MYANMAR = 13;
- public static final int SCRIPT_SINHALA = 14;
- public static final int SCRIPT_TAMIL = 15;
- public static final int SCRIPT_TELUGU = 16;
- public static final int SCRIPT_THAI = 17;
-
- private static final TreeMap<String, Integer> mLanguageCodeToScriptCode;
-
- static {
- mLanguageCodeToScriptCode = new TreeMap<>();
- mLanguageCodeToScriptCode.put("", SCRIPT_LATIN); // default
- mLanguageCodeToScriptCode.put("ar", SCRIPT_ARABIC);
- mLanguageCodeToScriptCode.put("hy", SCRIPT_ARMENIAN);
- mLanguageCodeToScriptCode.put("bn", SCRIPT_BENGALI);
- mLanguageCodeToScriptCode.put("bg", SCRIPT_CYRILLIC);
- mLanguageCodeToScriptCode.put("sr", SCRIPT_CYRILLIC);
- mLanguageCodeToScriptCode.put("ru", SCRIPT_CYRILLIC);
- mLanguageCodeToScriptCode.put("ka", SCRIPT_GEORGIAN);
- mLanguageCodeToScriptCode.put("el", SCRIPT_GREEK);
- mLanguageCodeToScriptCode.put("iw", SCRIPT_HEBREW);
- mLanguageCodeToScriptCode.put("km", SCRIPT_KHMER);
- mLanguageCodeToScriptCode.put("lo", SCRIPT_LAO);
- mLanguageCodeToScriptCode.put("ml", SCRIPT_MALAYALAM);
- mLanguageCodeToScriptCode.put("my", SCRIPT_MYANMAR);
- mLanguageCodeToScriptCode.put("si", SCRIPT_SINHALA);
- mLanguageCodeToScriptCode.put("ta", SCRIPT_TAMIL);
- mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU);
- mLanguageCodeToScriptCode.put("th", SCRIPT_THAI);
- }
-
- /*
- * Returns whether the code point is a letter that makes sense for the specified
- * locale for this spell checker.
- * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
- * and is limited to EFIGS languages and Russian.
- * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
- * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
- */
- public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) {
- switch (scriptId) {
- case SCRIPT_ARABIC:
- // Arabic letters can be in any of the following blocks:
- // Arabic U+0600..U+06FF
- // Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF
- // Arabic Extended-A U+08A0..U+08FF
- // Arabic Presentation Forms-A U+FB50..U+FDFF
- // Arabic Presentation Forms-B U+FE70..U+FEFF
- return (codePoint >= 0x600 && codePoint <= 0x6FF)
- || (codePoint >= 0x750 && codePoint <= 0x7BF)
- || (codePoint >= 0x8A0 && codePoint <= 0x8FF)
- || (codePoint >= 0xFB50 && codePoint <= 0xFDFF)
- || (codePoint >= 0xFE70 && codePoint <= 0xFEFF);
- case SCRIPT_ARMENIAN:
- // Armenian letters are in the Armenian unicode block, U+0530..U+058F and
- // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
- // of that block, which is U+FB13..U+FB17.
- return (codePoint >= 0x530 && codePoint <= 0x58F
- || codePoint >= 0xFB13 && codePoint <= 0xFB17);
- case SCRIPT_BENGALI:
- // Bengali unicode block is U+0980..U+09FF
- return (codePoint >= 0x980 && codePoint <= 0x9FF);
- case SCRIPT_CYRILLIC:
- // All Cyrillic characters are in the 400~52F block. There are some in the upper
- // Unicode range, but they are archaic characters that are not used in modern
- // Russian and are not used by our dictionary.
- return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
- case SCRIPT_DEVANAGARI:
- // Devanagari unicode block is +0900..U+097F
- return (codePoint >= 0x900 && codePoint <= 0x97F);
- case SCRIPT_GEORGIAN:
- // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
- // or Georgian supplement block, U+2D00..U+2D2F
- return (codePoint >= 0x10A0 && codePoint <= 0x10FF
- || codePoint >= 0x2D00 && codePoint <= 0x2D2F);
- case SCRIPT_GREEK:
- // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
- // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
- // Our dictionary also contains a few words with 0xF2; it would be best to check
- // if that's correct, but a web search does return results for these words so
- // they are probably okay.
- return (codePoint >= 0x370 && codePoint <= 0x3FF)
- || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
- || codePoint == 0xF2;
- case SCRIPT_HEBREW:
- // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF,
- // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the
- // Hebrew part of that block, which is U+FB1D..U+FB4F.
- return (codePoint >= 0x590 && codePoint <= 0x5FF
- || codePoint >= 0xFB1D && codePoint <= 0xFB4F);
- case SCRIPT_KANNADA:
- // Kannada unicode block is U+0C80..U+0CFF
- return (codePoint >= 0xC80 && codePoint <= 0xCFF);
- case SCRIPT_KHMER:
- // Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block
- // is U+19E0..U+19FF
- return (codePoint >= 0x1780 && codePoint <= 0x17FF
- || codePoint >= 0x19E0 && codePoint <= 0x19FF);
- case SCRIPT_LAO:
- // The Lao block is U+0E80..U+0EFF
- return (codePoint >= 0xE80 && codePoint <= 0xEFF);
- case SCRIPT_LATIN:
- // Our supported latin script dictionaries (EFIGS) at the moment only include
- // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
- // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
- // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
- // excluded from isLetter anyway.
- return codePoint <= 0x2AF && Character.isLetter(codePoint);
- case SCRIPT_MALAYALAM:
- // Malayalam unicode block is U+0D00..U+0D7F
- return (codePoint >= 0xD00 && codePoint <= 0xD7F);
- case SCRIPT_MYANMAR:
- // Myanmar has three unicode blocks :
- // Myanmar U+1000..U+109F
- // Myanmar extended-A U+AA60..U+AA7F
- // Myanmar extended-B U+A9E0..U+A9FF
- return (codePoint >= 0x1000 && codePoint <= 0x109F
- || codePoint >= 0xAA60 && codePoint <= 0xAA7F
- || codePoint >= 0xA9E0 && codePoint <= 0xA9FF);
- case SCRIPT_SINHALA:
- // Sinhala unicode block is U+0D80..U+0DFF
- return (codePoint >= 0xD80 && codePoint <= 0xDFF);
- case SCRIPT_TAMIL:
- // Tamil unicode block is U+0B80..U+0BFF
- return (codePoint >= 0xB80 && codePoint <= 0xBFF);
- case SCRIPT_TELUGU:
- // Telugu unicode block is U+0C00..U+0C7F
- return (codePoint >= 0xC00 && codePoint <= 0xC7F);
- case SCRIPT_THAI:
- // Thai unicode block is U+0E00..U+0E7F
- return (codePoint >= 0xE00 && codePoint <= 0xE7F);
- case SCRIPT_UNKNOWN:
- return true;
- default:
- // Should never come here
- throw new RuntimeException("Impossible value of script: " + scriptId);
- }
- }
-
- /**
- * @param locale spell checker locale
- * @return internal Latin IME script code that maps to a language code
- * {@see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes}
- */
- public static int getScriptFromSpellCheckerLocale(final Locale locale) {
- String language = locale.getLanguage();
- Integer script = mLanguageCodeToScriptCode.get(language);
- if (script == null) {
- // Default to Latin.
- script = mLanguageCodeToScriptCode.get("");
- }
- return script;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java
deleted file mode 100644
index c41817fe6..000000000
--- a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.SpannedString;
-import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
-import android.text.style.URLSpan;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class SpannableStringUtils {
- /**
- * Copies the spans from the region <code>start...end</code> in
- * <code>source</code> to the region
- * <code>destoff...destoff+end-start</code> in <code>dest</code>.
- * Spans in <code>source</code> that begin before <code>start</code>
- * or end after <code>end</code> but overlap this range are trimmed
- * as if they began at <code>start</code> or ended at <code>end</code>.
- * Only SuggestionSpans that don't have the SPAN_PARAGRAPH span are copied.
- *
- * This code is almost entirely taken from {@link TextUtils#copySpansFrom}, except for the
- * kind of span that is copied.
- *
- * @throws IndexOutOfBoundsException if any of the copied spans
- * are out of range in <code>dest</code>.
- */
- public static void copyNonParagraphSuggestionSpansFrom(Spanned source, int start, int end,
- Spannable dest, int destoff) {
- Object[] spans = source.getSpans(start, end, SuggestionSpan.class);
-
- for (int i = 0; i < spans.length; i++) {
- int fl = source.getSpanFlags(spans[i]);
- // We don't care about the PARAGRAPH flag in LatinIME code. However, if this flag
- // is set, Spannable#setSpan will throw an exception unless the span is on the edge
- // of a word. But the spans have been split into two by the getText{Before,After}Cursor
- // methods, so after concatenation they may end in the middle of a word.
- // Since we don't use them, we can just remove them and avoid crashing.
- fl &= ~Spanned.SPAN_PARAGRAPH;
-
- int st = source.getSpanStart(spans[i]);
- int en = source.getSpanEnd(spans[i]);
-
- if (st < start)
- st = start;
- if (en > end)
- en = end;
-
- dest.setSpan(spans[i], st - start + destoff, en - start + destoff,
- fl);
- }
- }
-
- /**
- * Returns a CharSequence concatenating the specified CharSequences, retaining their
- * SuggestionSpans that don't have the PARAGRAPH flag, but not other spans.
- *
- * This code is almost entirely taken from {@link TextUtils#concat(CharSequence...)}, except
- * it calls copyNonParagraphSuggestionSpansFrom instead of {@link TextUtils#copySpansFrom}.
- */
- public static CharSequence concatWithNonParagraphSuggestionSpansOnly(CharSequence... text) {
- if (text.length == 0) {
- return "";
- }
-
- if (text.length == 1) {
- return text[0];
- }
-
- boolean spanned = false;
- for (int i = 0; i < text.length; i++) {
- if (text[i] instanceof Spanned) {
- spanned = true;
- break;
- }
- }
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < text.length; i++) {
- sb.append(text[i]);
- }
-
- if (!spanned) {
- return sb.toString();
- }
-
- SpannableString ss = new SpannableString(sb);
- int off = 0;
- for (int i = 0; i < text.length; i++) {
- int len = text[i].length();
-
- if (text[i] instanceof Spanned) {
- copyNonParagraphSuggestionSpansFrom((Spanned) text[i], 0, len, ss, off);
- }
-
- off += len;
- }
-
- return new SpannedString(ss);
- }
-
- public static boolean hasUrlSpans(final CharSequence text,
- final int startIndex, final int endIndex) {
- if (!(text instanceof Spanned)) {
- return false; // Not spanned, so no link
- }
- final Spanned spanned = (Spanned)text;
- // getSpans(x, y) does not return spans that start on x or end on y. x-1, y+1 does the
- // trick, and works in all cases even if startIndex <= 0 or endIndex >= text.length().
- final URLSpan[] spans = spanned.getSpans(startIndex - 1, endIndex + 1, URLSpan.class);
- return null != spans && spans.length > 0;
- }
-
- /**
- * Splits the given {@code charSequence} with at occurrences of the given {@code regex}.
- * <p>
- * This is equivalent to
- * {@code charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0)}
- * except that the spans are preserved in the result array.
- * </p>
- * @param charSequence the character sequence to be split.
- * @param regex the regex pattern to be used as the separator.
- * @param preserveTrailingEmptySegments {@code true} to preserve the trailing empty
- * segments. Otherwise, trailing empty segments will be removed before being returned.
- * @return the array which contains the result. All the spans in the <code>charSequence</code>
- * is preserved.
- */
- @UsedForTesting
- public static CharSequence[] split(final CharSequence charSequence, final String regex,
- final boolean preserveTrailingEmptySegments) {
- // A short-cut for non-spanned strings.
- if (!(charSequence instanceof Spanned)) {
- // -1 means that trailing empty segments will be preserved.
- return charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0);
- }
-
- // Hereafter, emulate String.split for CharSequence.
- final ArrayList<CharSequence> sequences = new ArrayList<>();
- final Matcher matcher = Pattern.compile(regex).matcher(charSequence);
- int nextStart = 0;
- boolean matched = false;
- while (matcher.find()) {
- sequences.add(charSequence.subSequence(nextStart, matcher.start()));
- nextStart = matcher.end();
- matched = true;
- }
- if (!matched) {
- // never matched. preserveTrailingEmptySegments is ignored in this case.
- return new CharSequence[] { charSequence };
- }
- sequences.add(charSequence.subSequence(nextStart, charSequence.length()));
- if (!preserveTrailingEmptySegments) {
- for (int i = sequences.size() - 1; i >= 0; --i) {
- if (!TextUtils.isEmpty(sequences.get(i))) {
- break;
- }
- sequences.remove(i);
- }
- }
- return sequences.toArray(new CharSequence[sequences.size()]);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
deleted file mode 100644
index 03e58478b..000000000
--- a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.DictionaryFacilitator;
-import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-import javax.annotation.Nullable;
-
-@SuppressWarnings("unused")
-public final class StatsUtils {
-
- private StatsUtils() {
- // Intentional empty constructor.
- }
-
- public static void onCreate(final SettingsValues settingsValues,
- RichInputMethodManager richImm) {
- }
-
- public static void onPickSuggestionManually(final SuggestedWords suggestedWords,
- final SuggestedWords.SuggestedWordInfo suggestionInfo,
- final DictionaryFacilitator dictionaryFacilitator) {
- }
-
- public static void onBackspaceWordDelete(int wordLength) {
- }
-
- public static void onBackspacePressed(int lengthToDelete) {
- }
-
- public static void onBackspaceSelectedText(int selectedTextLength) {
- }
-
- public static void onDeleteMultiCharInput(int multiCharLength) {
- }
-
- public static void onRevertAutoCorrect() {
- }
-
- public static void onRevertDoubleSpacePeriod() {
- }
-
- public static void onRevertSwapPunctuation() {
- }
-
- public static void onFinishInputView() {
- }
-
- public static void onCreateInputView() {
- }
-
- public static void onStartInputView(int inputType, int displayOrientation, boolean restarting) {
- }
-
- public static void onAutoCorrection(final String typedWord, final String autoCorrectionWord,
- final boolean isBatchInput, final DictionaryFacilitator dictionaryFacilitator,
- final String prevWordsContext) {
- }
-
- public static void onWordCommitUserTyped(final String commitWord, final boolean isBatchMode) {
- }
-
- public static void onWordCommitAutoCorrect(final String commitWord, final boolean isBatchMode) {
- }
-
- public static void onWordCommitSuggestionPickedManually(
- final String commitWord, final boolean isBatchMode) {
- }
-
- public static void onDoubleSpacePeriod() {
- }
-
- public static void onLoadSettings(SettingsValues settingsValues) {
- }
-
- public static void onInvalidWordIdentification(final String invalidWord) {
- }
-
- public static void onSubtypeChanged(final InputMethodSubtype oldSubtype,
- final InputMethodSubtype newSubtype) {
- }
-
- public static void onSettingsActivity(final String entryPoint) {
- }
-
- public static void onInputConnectionLaggy(final int operation, final long duration) {
- }
-
- public static void onDecoderLaggy(final int operation, final long duration) {
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java b/java/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java
deleted file mode 100644
index cd42f50c7..000000000
--- a/java/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-
-import com.android.inputmethod.latin.DictionaryFacilitator;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
-@SuppressWarnings("unused")
-public class StatsUtilsManager {
-
- private static final StatsUtilsManager sInstance = new StatsUtilsManager();
- private static StatsUtilsManager sTestInstance = null;
-
- /**
- * @return the singleton instance of {@link StatsUtilsManager}.
- */
- public static StatsUtilsManager getInstance() {
- return sTestInstance != null ? sTestInstance : sInstance;
- }
-
- public static void setTestInstance(final StatsUtilsManager testInstance) {
- sTestInstance = testInstance;
- }
-
- public void onCreate(final Context context, final DictionaryFacilitator dictionaryFacilitator) {
- }
-
- public void onLoadSettings(final Context context, final SettingsValues settingsValues) {
- }
-
- public void onStartInputView() {
- }
-
- public void onFinishInputView() {
- }
-
- public void onDestroy(final Context context) {
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
deleted file mode 100644
index 54a3fc39c..000000000
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.COMBINING_RULES;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Build;
-import android.util.Log;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.LocaleUtils;
-import com.android.inputmethod.latin.common.StringUtils;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * A helper class to deal with subtype locales.
- */
-// TODO: consolidate this into RichInputMethodSubtype
-public final class SubtypeLocaleUtils {
- static final String TAG = SubtypeLocaleUtils.class.getSimpleName();
-
- // This reference class {@link R} must be located in the same package as LatinIME.java.
- private static final String RESOURCE_PACKAGE_NAME = R.class.getPackage().getName();
-
- // Special language code to represent "no language".
- public static final String NO_LANGUAGE = "zz";
- public static final String QWERTY = "qwerty";
- public static final String EMOJI = "emoji";
- public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic;
-
- private static volatile boolean sInitialized = false;
- private static final Object sInitializeLock = new Object();
- private static Resources sResources;
- // Keyboard layout to its display name map.
- private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>();
- // Keyboard layout to subtype name resource id map.
- private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>();
- // Exceptional locale whose name should be displayed in Locale.ROOT.
- private static final HashMap<String, Integer> sExceptionalLocaleDisplayedInRootLocale =
- new HashMap<>();
- // Exceptional locale to subtype name resource id map.
- private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>();
- // Exceptional locale to subtype name with layout resource id map.
- private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap =
- new HashMap<>();
- private static final String SUBTYPE_NAME_RESOURCE_PREFIX =
- "string/subtype_";
- private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX =
- "string/subtype_generic_";
- private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX =
- "string/subtype_with_layout_";
- private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX =
- "string/subtype_no_language_";
- private static final String SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX =
- "string/subtype_in_root_locale_";
- // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
- // This is for compatibility to keep the same subtype ids as pre-JellyBean.
- private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
- new HashMap<>();
-
- private SubtypeLocaleUtils() {
- // Intentional empty constructor for utility class.
- }
-
- // Note that this initialization method can be called multiple times.
- public static void init(final Context context) {
- synchronized (sInitializeLock) {
- if (sInitialized == false) {
- initLocked(context);
- sInitialized = true;
- }
- }
- }
-
- private static void initLocked(final Context context) {
- final Resources res = context.getResources();
- sResources = res;
-
- final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
- final String[] layoutDisplayNames = res.getStringArray(
- R.array.predefined_layout_display_names);
- for (int i = 0; i < predefinedLayoutSet.length; i++) {
- final String layoutName = predefinedLayoutSet[i];
- sKeyboardLayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]);
- final String resourceName = SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX + layoutName;
- final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
- sKeyboardLayoutToNameIdsMap.put(layoutName, resId);
- // Register subtype name resource id of "No language" with key "zz_<layout>"
- final String noLanguageResName = SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX + layoutName;
- final int noLanguageResId = res.getIdentifier(
- noLanguageResName, null, RESOURCE_PACKAGE_NAME);
- final String key = getNoLanguageLayoutKey(layoutName);
- sKeyboardLayoutToNameIdsMap.put(key, noLanguageResId);
- }
-
- final String[] exceptionalLocaleInRootLocale = res.getStringArray(
- R.array.subtype_locale_displayed_in_root_locale);
- for (int i = 0; i < exceptionalLocaleInRootLocale.length; i++) {
- final String localeString = exceptionalLocaleInRootLocale[i];
- final String resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + localeString;
- final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
- sExceptionalLocaleDisplayedInRootLocale.put(localeString, resId);
- }
-
- final String[] exceptionalLocales = res.getStringArray(
- R.array.subtype_locale_exception_keys);
- for (int i = 0; i < exceptionalLocales.length; i++) {
- final String localeString = exceptionalLocales[i];
- final String resourceName = SUBTYPE_NAME_RESOURCE_PREFIX + localeString;
- final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
- sExceptionalLocaleToNameIdsMap.put(localeString, resId);
- final String resourceNameWithLayout =
- SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + localeString;
- final int resIdWithLayout = res.getIdentifier(
- resourceNameWithLayout, null, RESOURCE_PACKAGE_NAME);
- sExceptionalLocaleToWithLayoutNameIdsMap.put(localeString, resIdWithLayout);
- }
-
- final String[] keyboardLayoutSetMap = res.getStringArray(
- R.array.locale_and_extra_value_to_keyboard_layout_set_map);
- for (int i = 0; i + 1 < keyboardLayoutSetMap.length; i += 2) {
- final String key = keyboardLayoutSetMap[i];
- final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1];
- sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet);
- }
- }
-
- public static boolean isExceptionalLocale(final String localeString) {
- return sExceptionalLocaleToNameIdsMap.containsKey(localeString);
- }
-
- private static final String getNoLanguageLayoutKey(final String keyboardLayoutName) {
- return NO_LANGUAGE + "_" + keyboardLayoutName;
- }
-
- public static int getSubtypeNameId(final String localeString, final String keyboardLayoutName) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
- && isExceptionalLocale(localeString)) {
- return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString);
- }
- final String key = NO_LANGUAGE.equals(localeString)
- ? getNoLanguageLayoutKey(keyboardLayoutName)
- : keyboardLayoutName;
- final Integer nameId = sKeyboardLayoutToNameIdsMap.get(key);
- return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId;
- }
-
- @Nonnull
- public static Locale getDisplayLocaleOfSubtypeLocale(@Nonnull final String localeString) {
- if (NO_LANGUAGE.equals(localeString)) {
- return sResources.getConfiguration().locale;
- }
- if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
- return Locale.ROOT;
- }
- return LocaleUtils.constructLocaleFromString(localeString);
- }
-
- public static String getSubtypeLocaleDisplayNameInSystemLocale(
- @Nonnull final String localeString) {
- final Locale displayLocale = sResources.getConfiguration().locale;
- return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale);
- }
-
- @Nonnull
- public static String getSubtypeLocaleDisplayName(@Nonnull final String localeString) {
- final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString);
- return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale);
- }
-
- @Nonnull
- public static String getSubtypeLanguageDisplayName(@Nonnull final String localeString) {
- final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString);
- final String languageString;
- if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
- languageString = localeString;
- } else {
- languageString = LocaleUtils.constructLocaleFromString(localeString).getLanguage();
- }
- return getSubtypeLocaleDisplayNameInternal(languageString, displayLocale);
- }
-
- @Nonnull
- private static String getSubtypeLocaleDisplayNameInternal(@Nonnull final String localeString,
- @Nonnull final Locale displayLocale) {
- if (NO_LANGUAGE.equals(localeString)) {
- // No language subtype should be displayed in system locale.
- return sResources.getString(R.string.subtype_no_language);
- }
- final Integer exceptionalNameResId;
- if (displayLocale.equals(Locale.ROOT)
- && sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
- exceptionalNameResId = sExceptionalLocaleDisplayedInRootLocale.get(localeString);
- } else if (sExceptionalLocaleToNameIdsMap.containsKey(localeString)) {
- exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString);
- } else {
- exceptionalNameResId = null;
- }
-
- final String displayName;
- if (exceptionalNameResId != null) {
- final RunInLocale<String> getExceptionalName = new RunInLocale<String>() {
- @Override
- protected String job(final Resources res) {
- return res.getString(exceptionalNameResId);
- }
- };
- displayName = getExceptionalName.runInLocale(sResources, displayLocale);
- } else {
- displayName = LocaleUtils.constructLocaleFromString(localeString)
- .getDisplayName(displayLocale);
- }
- return StringUtils.capitalizeFirstCodePoint(displayName, displayLocale);
- }
-
- // InputMethodSubtype's display name in its locale.
- // isAdditionalSubtype (T=true, F=false)
- // locale layout | display name
- // ------ ------- - ----------------------
- // en_US qwerty F English (US) exception
- // en_GB qwerty F English (UK) exception
- // es_US spanish F Español (EE.UU.) exception
- // fr azerty F Français
- // fr_CA qwerty F Français (Canada)
- // fr_CH swiss F Français (Suisse)
- // de qwertz F Deutsch
- // de_CH swiss T Deutsch (Schweiz)
- // zz qwerty F Alphabet (QWERTY) in system locale
- // fr qwertz T Français (QWERTZ)
- // de qwerty T Deutsch (QWERTY)
- // en_US azerty T English (US) (AZERTY) exception
- // zz azerty T Alphabet (AZERTY) in system locale
-
- @Nonnull
- private static String getReplacementString(@Nonnull final InputMethodSubtype subtype,
- @Nonnull final Locale displayLocale) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
- && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
- return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
- }
- return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale);
- }
-
- @Nonnull
- public static String getSubtypeDisplayNameInSystemLocale(
- @Nonnull final InputMethodSubtype subtype) {
- final Locale displayLocale = sResources.getConfiguration().locale;
- return getSubtypeDisplayNameInternal(subtype, displayLocale);
- }
-
- @Nonnull
- public static String getSubtypeNameForLogging(@Nullable final InputMethodSubtype subtype) {
- if (subtype == null) {
- return "<null subtype>";
- }
- return getSubtypeLocale(subtype) + "/" + getKeyboardLayoutSetName(subtype);
- }
-
- @Nonnull
- private static String getSubtypeDisplayNameInternal(@Nonnull final InputMethodSubtype subtype,
- @Nonnull final Locale displayLocale) {
- final String replacementString = getReplacementString(subtype, displayLocale);
- // TODO: rework this for multi-lingual subtypes
- final int nameResId = subtype.getNameResId();
- final RunInLocale<String> getSubtypeName = new RunInLocale<String>() {
- @Override
- protected String job(final Resources res) {
- try {
- return res.getString(nameResId, replacementString);
- } catch (Resources.NotFoundException e) {
- // TODO: Remove this catch when InputMethodManager.getCurrentInputMethodSubtype
- // is fixed.
- Log.w(TAG, "Unknown subtype: mode=" + subtype.getMode()
- + " nameResId=" + subtype.getNameResId()
- + " locale=" + subtype.getLocale()
- + " extra=" + subtype.getExtraValue()
- + "\n" + DebugLogUtils.getStackTrace());
- return "";
- }
- }
- };
- return StringUtils.capitalizeFirstCodePoint(
- getSubtypeName.runInLocale(sResources, displayLocale), displayLocale);
- }
-
- @Nonnull
- public static Locale getSubtypeLocale(@Nonnull final InputMethodSubtype subtype) {
- final String localeString = subtype.getLocale();
- return LocaleUtils.constructLocaleFromString(localeString);
- }
-
- @Nonnull
- public static String getKeyboardLayoutSetDisplayName(
- @Nonnull final InputMethodSubtype subtype) {
- final String layoutName = getKeyboardLayoutSetName(subtype);
- return getKeyboardLayoutSetDisplayName(layoutName);
- }
-
- @Nonnull
- public static String getKeyboardLayoutSetDisplayName(@Nonnull final String layoutName) {
- return sKeyboardLayoutToDisplayNameMap.get(layoutName);
- }
-
- @Nonnull
- public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) {
- String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
- if (keyboardLayoutSet == null) {
- // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard
- // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with
- // pre-JellyBean.
- final String key = subtype.getLocale() + ":" + subtype.getExtraValue();
- keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key);
- }
- // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
- // fixed.
- if (keyboardLayoutSet == null) {
- android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " +
- "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue());
- return QWERTY;
- }
- return keyboardLayoutSet;
- }
-
- public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) {
- return subtype.getExtraValueOf(COMBINING_RULES);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
deleted file mode 100644
index 981355115..000000000
--- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.define.ProductionFlags;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.TreeSet;
-
-/**
- * A TreeSet of SuggestedWordInfo that is bounded in size and throws everything that's smaller
- * than its limit
- */
-public final class SuggestionResults extends TreeSet<SuggestedWordInfo> {
- public final ArrayList<SuggestedWordInfo> mRawSuggestions;
- // TODO: Instead of a boolean , we may want to include the context of this suggestion results,
- // such as {@link NgramContext}.
- public final boolean mIsBeginningOfSentence;
- public final boolean mFirstSuggestionExceedsConfidenceThreshold;
- private final int mCapacity;
-
- public SuggestionResults(final int capacity, final boolean isBeginningOfSentence,
- final boolean firstSuggestionExceedsConfidenceThreshold) {
- this(sSuggestedWordInfoComparator, capacity, isBeginningOfSentence,
- firstSuggestionExceedsConfidenceThreshold);
- }
-
- private SuggestionResults(final Comparator<SuggestedWordInfo> comparator, final int capacity,
- final boolean isBeginningOfSentence,
- final boolean firstSuggestionExceedsConfidenceThreshold) {
- super(comparator);
- mCapacity = capacity;
- if (ProductionFlags.INCLUDE_RAW_SUGGESTIONS) {
- mRawSuggestions = new ArrayList<>();
- } else {
- mRawSuggestions = null;
- }
- mIsBeginningOfSentence = isBeginningOfSentence;
- mFirstSuggestionExceedsConfidenceThreshold = firstSuggestionExceedsConfidenceThreshold;
- }
-
- @Override
- public boolean add(final SuggestedWordInfo e) {
- if (size() < mCapacity) return super.add(e);
- if (comparator().compare(e, last()) > 0) return false;
- super.add(e);
- pollLast(); // removes the last element
- return true;
- }
-
- @Override
- public boolean addAll(final Collection<? extends SuggestedWordInfo> e) {
- if (null == e) return false;
- return super.addAll(e);
- }
-
- static final class SuggestedWordInfoComparator implements Comparator<SuggestedWordInfo> {
- // This comparator ranks the word info with the higher frequency first. That's because
- // that's the order we want our elements in.
- @Override
- public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) {
- if (o1.mScore > o2.mScore) return -1;
- if (o1.mScore < o2.mScore) return 1;
- if (o1.mCodePointCount < o2.mCodePointCount) return -1;
- if (o1.mCodePointCount > o2.mCodePointCount) return 1;
- return o1.mWord.compareTo(o2.mWord);
- }
- }
-
- private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
- new SuggestedWordInfoComparator();
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
deleted file mode 100644
index ab2b00e36..000000000
--- a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.util.LruCache;
-
-import com.android.inputmethod.compat.AppWorkaroundsUtils;
-
-public final class TargetPackageInfoGetterTask extends
- AsyncTask<String, Void, PackageInfo> {
- private static final int MAX_CACHE_ENTRIES = 64; // arbitrary
- private static final LruCache<String, PackageInfo> sCache = new LruCache<>(MAX_CACHE_ENTRIES);
-
- public static PackageInfo getCachedPackageInfo(final String packageName) {
- if (null == packageName) return null;
- return sCache.get(packageName);
- }
-
- public static void removeCachedPackageInfo(final String packageName) {
- sCache.remove(packageName);
- }
-
- private Context mContext;
- private final AsyncResultHolder<AppWorkaroundsUtils> mResult;
-
- public TargetPackageInfoGetterTask(final Context context,
- final AsyncResultHolder<AppWorkaroundsUtils> result) {
- mContext = context;
- mResult = result;
- }
-
- @Override
- protected PackageInfo doInBackground(final String... packageName) {
- final PackageManager pm = mContext.getPackageManager();
- mContext = null; // Bazooka-powered anti-leak device
- try {
- final PackageInfo packageInfo = pm.getPackageInfo(packageName[0], 0 /* flags */);
- sCache.put(packageName[0], packageInfo);
- return packageInfo;
- } catch (android.content.pm.PackageManager.NameNotFoundException e) {
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(final PackageInfo info) {
- mResult.set(new AppWorkaroundsUtils(info));
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java
deleted file mode 100644
index dbf3b5060..000000000
--- a/java/src/com/android/inputmethod/latin/utils/TextRange.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.text.Spanned;
-import android.text.style.SuggestionSpan;
-
-import java.util.Arrays;
-
-/**
- * Represents a range of text, relative to the current cursor position.
- */
-public final class TextRange {
- private final CharSequence mTextAtCursor;
- private final int mWordAtCursorStartIndex;
- private final int mWordAtCursorEndIndex;
- private final int mCursorIndex;
-
- public final CharSequence mWord;
- public final boolean mHasUrlSpans;
-
- public int getNumberOfCharsInWordBeforeCursor() {
- return mCursorIndex - mWordAtCursorStartIndex;
- }
-
- public int getNumberOfCharsInWordAfterCursor() {
- return mWordAtCursorEndIndex - mCursorIndex;
- }
-
- public int length() {
- return mWord.length();
- }
-
- /**
- * Gets the suggestion spans that are put squarely on the word, with the exact start
- * and end of the span matching the boundaries of the word.
- * @return the list of spans.
- */
- public SuggestionSpan[] getSuggestionSpansAtWord() {
- if (!(mTextAtCursor instanceof Spanned && mWord instanceof Spanned)) {
- return new SuggestionSpan[0];
- }
- final Spanned text = (Spanned)mTextAtCursor;
- // Note: it's fine to pass indices negative or greater than the length of the string
- // to the #getSpans() method. The reason we need to get from -1 to +1 is that, the
- // spans were cut at the cursor position, and #getSpans(start, end) does not return
- // spans that end at `start' or begin at `end'. Consider the following case:
- // this| is (The | symbolizes the cursor position
- // ---- ---
- // In this case, the cursor is in position 4, so the 0~7 span has been split into
- // a 0~4 part and a 4~7 part.
- // If we called #getSpans(0, 4) in this case, we would only get the part from 0 to 4
- // of the span, and not the part from 4 to 7, so we would not realize the span actually
- // extends from 0 to 7. But if we call #getSpans(-1, 5) we'll get both the 0~4 and
- // the 4~7 spans and we can merge them accordingly.
- // Any span starting more than 1 char away from the word boundaries in any direction
- // does not touch the word, so we don't need to consider it. That's why requesting
- // -1 ~ +1 is enough.
- // Of course this is only relevant if the cursor is at one end of the word. If it's
- // in the middle, the -1 and +1 are not necessary, but they are harmless.
- final SuggestionSpan[] spans = text.getSpans(mWordAtCursorStartIndex - 1,
- mWordAtCursorEndIndex + 1, SuggestionSpan.class);
- int readIndex = 0;
- int writeIndex = 0;
- for (; readIndex < spans.length; ++readIndex) {
- final SuggestionSpan span = spans[readIndex];
- // The span may be null, as we null them when we find duplicates. Cf a few lines
- // down.
- if (null == span) continue;
- // Tentative span start and end. This may be modified later if we realize the
- // same span is also applied to other parts of the string.
- int spanStart = text.getSpanStart(span);
- int spanEnd = text.getSpanEnd(span);
- for (int i = readIndex + 1; i < spans.length; ++i) {
- if (span.equals(spans[i])) {
- // We found the same span somewhere else. Read the new extent of this
- // span, and adjust our values accordingly.
- spanStart = Math.min(spanStart, text.getSpanStart(spans[i]));
- spanEnd = Math.max(spanEnd, text.getSpanEnd(spans[i]));
- // ...and mark the span as processed.
- spans[i] = null;
- }
- }
- if (spanStart == mWordAtCursorStartIndex && spanEnd == mWordAtCursorEndIndex) {
- // If the span does not start and stop here, ignore it. It probably extends
- // past the start or end of the word, as happens in missing space correction
- // or EasyEditSpans put by voice input.
- spans[writeIndex++] = spans[readIndex];
- }
- }
- return writeIndex == readIndex ? spans : Arrays.copyOfRange(spans, 0, writeIndex);
- }
-
- public TextRange(final CharSequence textAtCursor, final int wordAtCursorStartIndex,
- final int wordAtCursorEndIndex, final int cursorIndex, final boolean hasUrlSpans) {
- if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex
- || cursorIndex > wordAtCursorEndIndex
- || wordAtCursorEndIndex > textAtCursor.length()) {
- throw new IndexOutOfBoundsException();
- }
- mTextAtCursor = textAtCursor;
- mWordAtCursorStartIndex = wordAtCursorStartIndex;
- mWordAtCursorEndIndex = wordAtCursorEndIndex;
- mCursorIndex = cursorIndex;
- mHasUrlSpans = hasUrlSpans;
- mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex);
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
deleted file mode 100644
index fafba79c2..000000000
--- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.util.SparseArray;
-
-public final class TypefaceUtils {
- private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
- private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
-
- private TypefaceUtils() {
- // This utility class is not publicly instantiable.
- }
-
- // This sparse array caches key label text height in pixel indexed by key label text size.
- private static final SparseArray<Float> sTextHeightCache = new SparseArray<>();
- // Working variable for the following method.
- private static final Rect sTextHeightBounds = new Rect();
-
- private static float getCharHeight(final char[] referenceChar, final Paint paint) {
- final int key = getCharGeometryCacheKey(referenceChar[0], paint);
- synchronized (sTextHeightCache) {
- final Float cachedValue = sTextHeightCache.get(key);
- if (cachedValue != null) {
- return cachedValue;
- }
-
- paint.getTextBounds(referenceChar, 0, 1, sTextHeightBounds);
- final float height = sTextHeightBounds.height();
- sTextHeightCache.put(key, height);
- return height;
- }
- }
-
- // This sparse array caches key label text width in pixel indexed by key label text size.
- private static final SparseArray<Float> sTextWidthCache = new SparseArray<>();
- // Working variable for the following method.
- private static final Rect sTextWidthBounds = new Rect();
-
- private static float getCharWidth(final char[] referenceChar, final Paint paint) {
- final int key = getCharGeometryCacheKey(referenceChar[0], paint);
- synchronized (sTextWidthCache) {
- final Float cachedValue = sTextWidthCache.get(key);
- if (cachedValue != null) {
- return cachedValue;
- }
-
- paint.getTextBounds(referenceChar, 0, 1, sTextWidthBounds);
- final float width = sTextWidthBounds.width();
- sTextWidthCache.put(key, width);
- return width;
- }
- }
-
- private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
- final int labelSize = (int)paint.getTextSize();
- final Typeface face = paint.getTypeface();
- final int codePointOffset = referenceChar << 15;
- if (face == Typeface.DEFAULT) {
- return codePointOffset + labelSize;
- } else if (face == Typeface.DEFAULT_BOLD) {
- return codePointOffset + labelSize + 0x1000;
- } else if (face == Typeface.MONOSPACE) {
- return codePointOffset + labelSize + 0x2000;
- } else {
- return codePointOffset + labelSize;
- }
- }
-
- public static float getReferenceCharHeight(final Paint paint) {
- return getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint);
- }
-
- public static float getReferenceCharWidth(final Paint paint) {
- return getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint);
- }
-
- public static float getReferenceDigitWidth(final Paint paint) {
- return getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint);
- }
-
- // Working variable for the following method.
- private static final Rect sStringWidthBounds = new Rect();
-
- public static float getStringWidth(final String string, final Paint paint) {
- synchronized (sStringWidthBounds) {
- paint.getTextBounds(string, 0, string.length(), sStringWidthBounds);
- return sStringWidthBounds.width();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/UncachedInputMethodManagerUtils.java b/java/src/com/android/inputmethod/latin/utils/UncachedInputMethodManagerUtils.java
deleted file mode 100644
index 5df00efb9..000000000
--- a/java/src/com/android/inputmethod/latin/utils/UncachedInputMethodManagerUtils.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-import android.provider.Settings;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-
-/*
- * A utility class for {@link InputMethodManager}. Unlike {@link RichInputMethodManager}, this
- * class provides synchronous, non-cached access to {@link InputMethodManager}. The setup activity
- * is a good example to use this class because {@link InputMethodManagerService} may not be aware of
- * this IME immediately after this IME is installed.
- */
-public final class UncachedInputMethodManagerUtils {
- /**
- * Check if the IME specified by the context is enabled.
- * CAVEAT: This may cause a round trip IPC.
- *
- * @param context package context of the IME to be checked.
- * @param imm the {@link InputMethodManager}.
- * @return true if this IME is enabled.
- */
- public static boolean isThisImeEnabled(final Context context,
- final InputMethodManager imm) {
- final String packageName = context.getPackageName();
- for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) {
- if (packageName.equals(imi.getPackageName())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if the IME specified by the context is the current IME.
- * CAVEAT: This may cause a round trip IPC.
- *
- * @param context package context of the IME to be checked.
- * @param imm the {@link InputMethodManager}.
- * @return true if this IME is the current IME.
- */
- public static boolean isThisImeCurrent(final Context context,
- final InputMethodManager imm) {
- final InputMethodInfo imi = getInputMethodInfoOf(context.getPackageName(), imm);
- final String currentImeId = Settings.Secure.getString(
- context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- return imi != null && imi.getId().equals(currentImeId);
- }
-
- /**
- * Get {@link InputMethodInfo} of the IME specified by the package name.
- * CAVEAT: This may cause a round trip IPC.
- *
- * @param packageName package name of the IME.
- * @param imm the {@link InputMethodManager}.
- * @return the {@link InputMethodInfo} of the IME specified by the <code>packageName</code>,
- * or null if not found.
- */
- public static InputMethodInfo getInputMethodInfoOf(final String packageName,
- final InputMethodManager imm) {
- for (final InputMethodInfo imi : imm.getInputMethodList()) {
- if (packageName.equals(imi.getPackageName())) {
- return imi;
- }
- }
- return null;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java
deleted file mode 100644
index 0bcba2754..000000000
--- a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-
-public final class ViewLayoutUtils {
- private ViewLayoutUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static MarginLayoutParams newLayoutParam(final ViewGroup placer, final int width,
- final int height) {
- if (placer instanceof FrameLayout) {
- return new FrameLayout.LayoutParams(width, height);
- } else if (placer instanceof RelativeLayout) {
- return new RelativeLayout.LayoutParams(width, height);
- } else if (placer == null) {
- throw new NullPointerException("placer is null");
- } else {
- throw new IllegalArgumentException("placer is neither FrameLayout nor RelativeLayout: "
- + placer.getClass().getName());
- }
- }
-
- public static void placeViewAt(final View view, final int x, final int y, final int w,
- final int h) {
- final ViewGroup.LayoutParams lp = view.getLayoutParams();
- if (lp instanceof MarginLayoutParams) {
- final MarginLayoutParams marginLayoutParams = (MarginLayoutParams)lp;
- marginLayoutParams.width = w;
- marginLayoutParams.height = h;
- marginLayoutParams.setMargins(x, y, 0, 0);
- }
- }
-
- public static void updateLayoutHeightOf(final Window window, final int layoutHeight) {
- final WindowManager.LayoutParams params = window.getAttributes();
- if (params != null && params.height != layoutHeight) {
- params.height = layoutHeight;
- window.setAttributes(params);
- }
- }
-
- public static void updateLayoutHeightOf(final View view, final int layoutHeight) {
- final ViewGroup.LayoutParams params = view.getLayoutParams();
- if (params != null && params.height != layoutHeight) {
- params.height = layoutHeight;
- view.setLayoutParams(params);
- }
- }
-
- public static void updateLayoutGravityOf(final View view, final int layoutGravity) {
- final ViewGroup.LayoutParams lp = view.getLayoutParams();
- if (lp instanceof LinearLayout.LayoutParams) {
- final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)lp;
- if (params.gravity != layoutGravity) {
- params.gravity = layoutGravity;
- view.setLayoutParams(params);
- }
- } else if (lp instanceof FrameLayout.LayoutParams) {
- final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)lp;
- if (params.gravity != layoutGravity) {
- params.gravity = layoutGravity;
- view.setLayoutParams(params);
- }
- } else {
- throw new IllegalArgumentException("Layout parameter doesn't have gravity: "
- + lp.getClass().getName());
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java b/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java
deleted file mode 100644
index fc0a9cb6c..000000000
--- a/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.NgramContext;
-import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.define.DecoderSpecificConstants;
-import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-// Note: this class is used as a parameter type of a native method. You should be careful when you
-// rename this class or field name. See BinaryDictionary#addMultipleDictionaryEntriesNative().
-public final class WordInputEventForPersonalization {
- private static final String TAG = WordInputEventForPersonalization.class.getSimpleName();
- private static final boolean DEBUG_TOKEN = false;
-
- public final int[] mTargetWord;
- public final int mPrevWordsCount;
- public final int[][] mPrevWordArray =
- new int[DecoderSpecificConstants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][];
- public final boolean[] mIsPrevWordBeginningOfSentenceArray =
- new boolean[DecoderSpecificConstants.MAX_PREV_WORD_COUNT_FOR_N_GRAM];
- // Time stamp in seconds.
- public final int mTimestamp;
-
- @UsedForTesting
- public WordInputEventForPersonalization(final CharSequence targetWord,
- final NgramContext ngramContext, final int timestamp) {
- mTargetWord = StringUtils.toCodePointArray(targetWord);
- mPrevWordsCount = ngramContext.getPrevWordCount();
- ngramContext.outputToArray(mPrevWordArray, mIsPrevWordBeginningOfSentenceArray);
- mTimestamp = timestamp;
- }
-
- // Process a list of words and return a list of {@link WordInputEventForPersonalization}
- // objects.
- public static ArrayList<WordInputEventForPersonalization> createInputEventFrom(
- final List<String> tokens, final int timestamp,
- final SpacingAndPunctuations spacingAndPunctuations, final Locale locale) {
- final ArrayList<WordInputEventForPersonalization> inputEvents = new ArrayList<>();
- final int N = tokens.size();
- NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO;
- for (int i = 0; i < N; ++i) {
- final String tempWord = tokens.get(i);
- if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) {
- // just skip this token
- if (DEBUG_TOKEN) {
- Log.d(TAG, "--- isEmptyStringOrWhiteSpaces: \"" + tempWord + "\"");
- }
- continue;
- }
- if (!DictionaryInfoUtils.looksValidForDictionaryInsertion(
- tempWord, spacingAndPunctuations)) {
- if (DEBUG_TOKEN) {
- Log.d(TAG, "--- not looksValidForDictionaryInsertion: \""
- + tempWord + "\"");
- }
- // Sentence terminator found. Split.
- // TODO: Detect whether the context is beginning-of-sentence.
- ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO;
- continue;
- }
- if (DEBUG_TOKEN) {
- Log.d(TAG, "--- word: \"" + tempWord + "\"");
- }
- final WordInputEventForPersonalization inputEvent =
- detectWhetherVaildWordOrNotAndGetInputEvent(
- ngramContext, tempWord, timestamp, locale);
- if (inputEvent == null) {
- continue;
- }
- inputEvents.add(inputEvent);
- ngramContext = ngramContext.getNextNgramContext(new NgramContext.WordInfo(tempWord));
- }
- return inputEvents;
- }
-
- private static WordInputEventForPersonalization detectWhetherVaildWordOrNotAndGetInputEvent(
- final NgramContext ngramContext, final String targetWord, final int timestamp,
- final Locale locale) {
- if (locale == null) {
- return null;
- }
- return new WordInputEventForPersonalization(targetWord, ngramContext, timestamp);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/utils/XmlParseUtils.java
deleted file mode 100644
index bdad16652..000000000
--- a/java/src/com/android/inputmethod/latin/utils/XmlParseUtils.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.res.TypedArray;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-public final class XmlParseUtils {
- private XmlParseUtils() {
- // This utility class is not publicly instantiable.
- }
-
- @SuppressWarnings("serial")
- public static class ParseException extends XmlPullParserException {
- public ParseException(final String msg, final XmlPullParser parser) {
- super(msg + " at " + parser.getPositionDescription());
- }
- }
-
- @SuppressWarnings("serial")
- public static final class IllegalStartTag extends ParseException {
- public IllegalStartTag(final XmlPullParser parser, final String tag, final String parent) {
- super("Illegal start tag " + tag + " in " + parent, parser);
- }
- }
-
- @SuppressWarnings("serial")
- public static final class IllegalEndTag extends ParseException {
- public IllegalEndTag(final XmlPullParser parser, final String tag, final String parent) {
- super("Illegal end tag " + tag + " in " + parent, parser);
- }
- }
-
- @SuppressWarnings("serial")
- public static final class IllegalAttribute extends ParseException {
- public IllegalAttribute(final XmlPullParser parser, final String tag,
- final String attribute) {
- super("Tag " + tag + " has illegal attribute " + attribute, parser);
- }
- }
-
- @SuppressWarnings("serial")
- public static final class NonEmptyTag extends ParseException{
- public NonEmptyTag(final XmlPullParser parser, final String tag) {
- super(tag + " must be empty tag", parser);
- }
- }
-
- public static void checkEndTag(final String tag, final XmlPullParser parser)
- throws XmlPullParserException, IOException {
- if (parser.next() == XmlPullParser.END_TAG && tag.equals(parser.getName()))
- return;
- throw new NonEmptyTag(parser, tag);
- }
-
- public static void checkAttributeExists(final TypedArray attr, final int attrId,
- final String attrName, final String tag, final XmlPullParser parser)
- throws XmlPullParserException {
- if (attr.hasValue(attrId)) {
- return;
- }
- throw new ParseException(
- "No " + attrName + " attribute found in <" + tag + "/>", parser);
- }
-}