diff options
Diffstat (limited to 'java/src')
45 files changed, 1883 insertions, 1458 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java index 3bed2c7a0..d5e638e7e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java @@ -28,8 +28,8 @@ import android.util.Log; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; -import com.android.inputmethod.latin.utils.Utils; import java.util.LinkedList; import java.util.Queue; @@ -144,7 +144,7 @@ public final class ActionBatch { // 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() - + Utils.getVersionName(context) + ".dict"; + + ApplicationUtils.getVersionName(context) + ".dict"; final Uri uri = Uri.parse(mWordList.mRemoteFilename + disambiguator); final Request request = new Request(uri); diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java index 6e3dd7109..767f895dc 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java @@ -22,7 +22,6 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; -import android.text.format.DateUtils; import android.util.Log; import android.widget.Toast; @@ -30,6 +29,7 @@ import com.android.inputmethod.latin.R; import java.util.Locale; import java.util.Random; +import java.util.concurrent.TimeUnit; /** * Service that handles background tasks for the dictionary provider. @@ -77,19 +77,19 @@ public final class DictionaryService extends Service { * 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 = 4 * DateUtils.DAY_IN_MILLIS; + private static final long UPDATE_FREQUENCY = 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 = 6 * ((int)AlarmManager.INTERVAL_HOUR); + private static final int MAX_ALARM_DELAY = (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 = 14 * DateUtils.DAY_IN_MILLIS; + private static final long VERY_LONG_TIME = TimeUnit.DAYS.toMillis(14); /** * The last seen start Id. This must be stored because we must only call stopSelfResult() with @@ -170,7 +170,7 @@ public final class DictionaryService extends Service { checkTimeAndMaybeSetupUpdateAlarm(context); } else if (DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION.equals(intent.getAction())) { // Intent to trigger an update now. - UpdateHandler.update(context, false); + UpdateHandler.tryUpdate(context, false); } else { UpdateHandler.downloadFinished(context, intent); } @@ -221,7 +221,7 @@ public final class DictionaryService extends Service { */ public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) { if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME)) return; - UpdateHandler.update(context, false); + UpdateHandler.tryUpdate(context, false); } /** diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index 4b89d20bb..7bbd041e7 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; +import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import android.view.animation.AnimationUtils; @@ -104,9 +105,16 @@ public final class DictionarySettingsFragment extends PreferenceFragment @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - mUpdateNowMenu = menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now); - mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - refreshNetworkState(); + final String metadataUri = + MetadataDbHelper.getMetadataUriAsString(getActivity(), mClientId); + // 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)) { + mUpdateNowMenu = + menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now); + mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + refreshNetworkState(); + } } @Override @@ -353,7 +361,12 @@ public final class DictionarySettingsFragment extends PreferenceFragment new Thread("updateByHand") { @Override public void run() { - UpdateHandler.update(activity, true); + // 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, true)) { + stopLoadingAnimation(); + } } }.start(); } @@ -368,7 +381,9 @@ public final class DictionarySettingsFragment extends PreferenceFragment private void startLoadingAnimation() { mLoadingView.setVisibility(View.VISIBLE); getView().setVisibility(View.GONE); - mUpdateNowMenu.setTitle(R.string.cancel); + // 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); } private void stopLoadingAnimation() { diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index 719f24e59..f66ef8733 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -38,8 +38,8 @@ import android.util.Log; import com.android.inputmethod.compat.ConnectivityManagerCompatUtils; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; -import com.android.inputmethod.latin.utils.Utils; import java.io.File; import java.io.FileInputStream; @@ -173,14 +173,15 @@ public final class UpdateHandler { * Download latest metadata from the server through DownloadManager for all known clients * @param context The context for retrieving resources * @param updateNow Whether we should update NOW, or respect bandwidth policies + * @return true if an update successfully started, false otherwise. */ - public static void update(final Context context, final boolean updateNow) { + public static boolean tryUpdate(final Context context, final boolean updateNow) { // TODO: loop through all clients instead of only doing the default one. final TreeSet<String> uris = new TreeSet<String>(); final Cursor cursor = MetadataDbHelper.queryClientIds(context); - if (null == cursor) return; + if (null == cursor) return false; try { - if (!cursor.moveToFirst()) return; + if (!cursor.moveToFirst()) return false; do { final String clientId = cursor.getString(0); final String metadataUri = @@ -192,6 +193,7 @@ public final class UpdateHandler { } 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. @@ -200,8 +202,10 @@ public final class UpdateHandler { // 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, updateNow, metadataUri); + started = true; } } + return started; } /** @@ -218,7 +222,7 @@ public final class UpdateHandler { // 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() - + Utils.getVersionName(context) + ".json"; + + ApplicationUtils.getVersionName(context) + ".json"; final Request metadataRequest = new Request(Uri.parse(metadataUri + disambiguator)); DebugLogUtils.l("Request =", metadataRequest); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 9eeee5baf..00ea20d2c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -20,7 +20,6 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.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. @@ -99,9 +98,9 @@ public interface KeyboardActionListener { */ public boolean onCustomRequest(int requestCode); - public static class Adapter implements KeyboardActionListener { - public static final Adapter EMPTY_LISTENER = new Adapter(); + public static final KeyboardActionListener EMPTY_LISTENER = new Adapter(); + public static class Adapter implements KeyboardActionListener { @Override public void onPressKey(int primaryCode, boolean isSinglePointer) {} @Override diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 4323f7171..98e2baee2 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -31,7 +31,6 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.keyboard.internal.KeyboardState; -import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputView; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; @@ -210,7 +209,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } public void onPressKey(final int code, final boolean isSinglePointer) { - hapticAndAudioFeedback(code); mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState()); } @@ -299,13 +297,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { ? keyboardView.getTimerProxy().isInDoubleTapShiftKeyTimeout() : false; } - private void hapticAndAudioFeedback(final int code) { - if (mKeyboardView == null || mKeyboardView.isInSlidingKeyInput()) { - return; - } - AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, mKeyboardView); - } - /** * Updates state machine to figure out when to automatically switch back to the previous mode. */ diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 4cee4cfa6..254b20b87 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -37,6 +37,7 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.TypefaceUtils; import com.android.inputmethod.research.ResearchLogger; import java.util.HashSet; diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 8a926d655..f85e60449 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -57,10 +57,8 @@ import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; import com.android.inputmethod.keyboard.internal.PreviewPlacerView; import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview; import com.android.inputmethod.keyboard.internal.TouchScreenRegulator; -import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.DebugSettings; -import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; @@ -72,7 +70,9 @@ import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.utils.StringUtils; -import com.android.inputmethod.latin.utils.Utils.UsabilityStudyLogUtils; +import com.android.inputmethod.latin.utils.TypefaceUtils; +import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils; +import com.android.inputmethod.latin.utils.ViewLayoutUtils; import com.android.inputmethod.research.ResearchLogger; import java.util.Locale; @@ -240,11 +240,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack break; case MSG_REPEAT_KEY: final Key currentKey = tracker.getKey(); - if (currentKey != null && currentKey.mCode == msg.arg1) { - tracker.onRepeatKey(currentKey); - AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback( - currentKey.mCode, keyboardView); + final int code = msg.arg1; + if (currentKey != null && currentKey.mCode == code) { startKeyRepeatTimer(tracker, mKeyRepeatInterval); + startTypingStateTimer(currentKey); + final KeyboardActionListener listener = + keyboardView.getKeyboardActionListener(); + listener.onPressKey(code, true /* isSinglePointer */); + listener.onCodeInput(code, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } break; case MSG_LONGPRESS_KEY: @@ -564,6 +568,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack altCodeKeyWhileTypingFadeoutAnimatorResId, this); mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator( altCodeKeyWhileTypingFadeinAnimatorResId, this); + + mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; } private ObjectAnimator loadObjectAnimator(final int resId, final Object target) { @@ -977,39 +983,28 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.mainKeyboardView_onLongPress(); } - final int code = key.mCode; + final KeyboardActionListener listener = mKeyboardActionListener; if (key.hasNoPanelAutoMoreKey()) { - final int embeddedCode = key.mMoreKeys[0].mCode; + final int moreKeyCode = key.mMoreKeys[0].mCode; tracker.onLongPressed(); - invokeCodeInput(embeddedCode); - invokeReleaseKey(code); - AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, this); + listener.onPressKey(moreKeyCode, true /* isSinglePointer */); + listener.onCodeInput(moreKeyCode, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); + listener.onReleaseKey(moreKeyCode, false /* withSliding */); return; } + final int code = key.mCode; if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) { // Long pressing the space key invokes IME switcher dialog. - if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { + if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) { tracker.onLongPressed(); - invokeReleaseKey(code); + listener.onReleaseKey(code, false /* withSliding */); return; } } openMoreKeysPanel(key, tracker); } - private boolean invokeCustomRequest(final int requestCode) { - return mKeyboardActionListener.onCustomRequest(requestCode); - } - - private void invokeCodeInput(final int code) { - mKeyboardActionListener.onCodeInput( - code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } - - private void invokeReleaseKey(final int code) { - mKeyboardActionListener.onReleaseKey(code, false); - } - private void openMoreKeysPanel(final Key key, final PointerTracker tracker) { final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext()); if (moreKeysPanel == null) { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index 51f5446db..d29e6382c 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -29,6 +29,7 @@ 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.utils.StringUtils; +import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreKeysKeyboard extends Keyboard { private final int mDefaultKeyCoordX; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 7b14259a0..53207597a 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -175,7 +175,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private DrawingProxy mDrawingProxy; private TimerProxy mTimerProxy; private KeyDetector mKeyDetector; - private KeyboardActionListener mListener = KeyboardActionListener.Adapter.EMPTY_LISTENER; + private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER; private Keyboard mKeyboard; private int mPhantonSuddenMoveThreshold; @@ -1263,13 +1263,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (!key.isRepeatable()) return; // Don't start key repeat when we are in sliding input mode. if (mIsInSlidingKeyInput) return; - onRepeatKey(key); - mTimerProxy.startKeyRepeatTimer(this); - } - - public void onRepeatKey(final Key key) { detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis()); - mTimerProxy.startTypingStateTimer(key); + mTimerProxy.startKeyRepeatTimer(this); } private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 370d0cb93..8ead44c31 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -81,9 +81,6 @@ public final class KeyboardState { private boolean mPrevSymbolsKeyboardWasShifted; private int mRecapitalizeMode; - // For handling long press. - private boolean mLongPressShiftLockFired; - // For handling double tap. private boolean mIsInAlphabetUnshiftedFromShifted; private boolean mIsInDoubleTapShiftKey; @@ -325,10 +322,11 @@ public final class KeyboardState { } 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(); } else { - mLongPressShiftLockFired = false; mShiftKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed(); // It is required to reset the auto caps state when all of the following conditions @@ -356,6 +354,8 @@ public final class KeyboardState { } if (code == Constants.CODE_SHIFT) { onReleaseShift(withSliding); + } else if (code == Constants.CODE_CAPSLOCK) { + setShiftLocked(!mAlphabetShiftState.isShiftLocked()); } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { onReleaseSymbol(withSliding); } @@ -437,7 +437,6 @@ public final class KeyboardState { } private void onPressShift() { - mLongPressShiftLockFired = false; // 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) { @@ -499,8 +498,6 @@ public final class KeyboardState { // Double tap shift key has been handled in {@link #onPressShift}, so that just // ignore this release shift key here. mIsInDoubleTapShiftKey = false; - } else if (mLongPressShiftLockFired) { - setShiftLocked(!mAlphabetShiftState.isShiftLocked()); } else if (mShiftKeyState.isChording()) { if (mAlphabetShiftState.isShiftLockShifted()) { // After chording input while shift locked state. @@ -610,12 +607,6 @@ public final class KeyboardState { break; } - if (code == Constants.CODE_CAPSLOCK) { - // Changing shift lock state will be handled at {@link #onPressShift()} when the shift - // key is released. - mLongPressShiftLockFired = true; - } - // If the code is a letter, update keyboard shift state. if (Constants.isLetterCode(code)) { updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 467c15ffd..1594df75b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -133,122 +133,125 @@ public final class KeyboardTextsSet { /* 28 */ "keylabel_for_east_slavic_row2_11", /* 29 */ "keylabel_for_east_slavic_row3_5", /* 30 */ "more_keys_for_cyrillic_u", - /* 31 */ "more_keys_for_cyrillic_en", - /* 32 */ "more_keys_for_cyrillic_ghe", - /* 33 */ "more_keys_for_east_slavic_row2_1", - /* 34 */ "more_keys_for_cyrillic_o", - /* 35 */ "more_keys_for_cyrillic_soft_sign", - /* 36 */ "keylabel_for_south_slavic_row1_6", - /* 37 */ "keylabel_for_south_slavic_row2_11", - /* 38 */ "keylabel_for_south_slavic_row3_1", - /* 39 */ "keylabel_for_south_slavic_row3_8", - /* 40 */ "more_keys_for_cyrillic_ie", - /* 41 */ "more_keys_for_cyrillic_i", - /* 42 */ "label_to_alpha_key", - /* 43 */ "single_quotes", - /* 44 */ "double_quotes", - /* 45 */ "single_angle_quotes", - /* 46 */ "double_angle_quotes", - /* 47 */ "more_keys_for_currency_dollar", - /* 48 */ "keylabel_for_currency_generic", - /* 49 */ "more_keys_for_currency_generic", - /* 50 */ "more_keys_for_punctuation", - /* 51 */ "more_keys_for_star", - /* 52 */ "more_keys_for_bullet", - /* 53 */ "more_keys_for_plus", - /* 54 */ "more_keys_for_left_parenthesis", - /* 55 */ "more_keys_for_right_parenthesis", - /* 56 */ "more_keys_for_less_than", - /* 57 */ "more_keys_for_greater_than", - /* 58 */ "more_keys_for_arabic_diacritics", - /* 59 */ "keyhintlabel_for_arabic_diacritics", - /* 60 */ "keylabel_for_symbols_1", - /* 61 */ "keylabel_for_symbols_2", - /* 62 */ "keylabel_for_symbols_3", - /* 63 */ "keylabel_for_symbols_4", - /* 64 */ "keylabel_for_symbols_5", - /* 65 */ "keylabel_for_symbols_6", - /* 66 */ "keylabel_for_symbols_7", - /* 67 */ "keylabel_for_symbols_8", - /* 68 */ "keylabel_for_symbols_9", - /* 69 */ "keylabel_for_symbols_0", - /* 70 */ "label_to_symbol_key", - /* 71 */ "label_to_symbol_with_microphone_key", - /* 72 */ "additional_more_keys_for_symbols_1", - /* 73 */ "additional_more_keys_for_symbols_2", - /* 74 */ "additional_more_keys_for_symbols_3", - /* 75 */ "additional_more_keys_for_symbols_4", - /* 76 */ "additional_more_keys_for_symbols_5", - /* 77 */ "additional_more_keys_for_symbols_6", - /* 78 */ "additional_more_keys_for_symbols_7", - /* 79 */ "additional_more_keys_for_symbols_8", - /* 80 */ "additional_more_keys_for_symbols_9", - /* 81 */ "additional_more_keys_for_symbols_0", - /* 82 */ "more_keys_for_symbols_1", - /* 83 */ "more_keys_for_symbols_2", - /* 84 */ "more_keys_for_symbols_3", - /* 85 */ "more_keys_for_symbols_4", - /* 86 */ "more_keys_for_symbols_5", - /* 87 */ "more_keys_for_symbols_6", - /* 88 */ "more_keys_for_symbols_7", - /* 89 */ "more_keys_for_symbols_8", - /* 90 */ "more_keys_for_symbols_9", - /* 91 */ "more_keys_for_symbols_0", - /* 92 */ "keylabel_for_comma", - /* 93 */ "more_keys_for_comma", - /* 94 */ "keylabel_for_symbols_question", - /* 95 */ "keylabel_for_symbols_semicolon", - /* 96 */ "keylabel_for_symbols_percent", - /* 97 */ "more_keys_for_symbols_exclamation", - /* 98 */ "more_keys_for_symbols_question", - /* 99 */ "more_keys_for_symbols_semicolon", - /* 100 */ "more_keys_for_symbols_percent", - /* 101 */ "keylabel_for_tablet_comma", - /* 102 */ "keyhintlabel_for_tablet_comma", - /* 103 */ "more_keys_for_tablet_comma", - /* 104 */ "keyhintlabel_for_tablet_period", - /* 105 */ "more_keys_for_tablet_period", - /* 106 */ "keylabel_for_apostrophe", - /* 107 */ "keyhintlabel_for_apostrophe", - /* 108 */ "more_keys_for_apostrophe", - /* 109 */ "more_keys_for_q", - /* 110 */ "more_keys_for_x", - /* 111 */ "keylabel_for_q", - /* 112 */ "keylabel_for_w", - /* 113 */ "keylabel_for_y", - /* 114 */ "keylabel_for_x", - /* 115 */ "keylabel_for_spanish_row2_10", - /* 116 */ "more_keys_for_am_pm", - /* 117 */ "settings_as_more_key", - /* 118 */ "shortcut_as_more_key", - /* 119 */ "action_next_as_more_key", - /* 120 */ "action_previous_as_more_key", - /* 121 */ "label_to_more_symbol_key", - /* 122 */ "label_to_more_symbol_for_tablet_key", - /* 123 */ "label_tab_key", - /* 124 */ "label_to_phone_numeric_key", - /* 125 */ "label_to_phone_symbols_key", - /* 126 */ "label_time_am", - /* 127 */ "label_time_pm", - /* 128 */ "label_to_symbol_key_pcqwerty", - /* 129 */ "keylabel_for_popular_domain", - /* 130 */ "more_keys_for_popular_domain", - /* 131 */ "more_keys_for_smiley", - /* 132 */ "single_laqm_raqm", - /* 133 */ "single_laqm_raqm_rtl", - /* 134 */ "single_raqm_laqm", - /* 135 */ "double_laqm_raqm", - /* 136 */ "double_laqm_raqm_rtl", - /* 137 */ "double_raqm_laqm", - /* 138 */ "single_lqm_rqm", - /* 139 */ "single_9qm_lqm", - /* 140 */ "single_9qm_rqm", - /* 141 */ "double_lqm_rqm", - /* 142 */ "double_9qm_lqm", - /* 143 */ "double_9qm_rqm", - /* 144 */ "more_keys_for_single_quote", - /* 145 */ "more_keys_for_double_quote", - /* 146 */ "more_keys_for_tablet_double_quote", + /* 31 */ "more_keys_for_cyrillic_ka", + /* 32 */ "more_keys_for_cyrillic_en", + /* 33 */ "more_keys_for_cyrillic_ghe", + /* 34 */ "more_keys_for_east_slavic_row2_1", + /* 35 */ "more_keys_for_cyrillic_a", + /* 36 */ "more_keys_for_cyrillic_o", + /* 37 */ "more_keys_for_cyrillic_soft_sign", + /* 38 */ "more_keys_for_east_slavic_row2_11", + /* 39 */ "keylabel_for_south_slavic_row1_6", + /* 40 */ "keylabel_for_south_slavic_row2_11", + /* 41 */ "keylabel_for_south_slavic_row3_1", + /* 42 */ "keylabel_for_south_slavic_row3_8", + /* 43 */ "more_keys_for_cyrillic_ie", + /* 44 */ "more_keys_for_cyrillic_i", + /* 45 */ "label_to_alpha_key", + /* 46 */ "single_quotes", + /* 47 */ "double_quotes", + /* 48 */ "single_angle_quotes", + /* 49 */ "double_angle_quotes", + /* 50 */ "more_keys_for_currency_dollar", + /* 51 */ "keylabel_for_currency_generic", + /* 52 */ "more_keys_for_currency_generic", + /* 53 */ "more_keys_for_punctuation", + /* 54 */ "more_keys_for_star", + /* 55 */ "more_keys_for_bullet", + /* 56 */ "more_keys_for_plus", + /* 57 */ "more_keys_for_left_parenthesis", + /* 58 */ "more_keys_for_right_parenthesis", + /* 59 */ "more_keys_for_less_than", + /* 60 */ "more_keys_for_greater_than", + /* 61 */ "more_keys_for_arabic_diacritics", + /* 62 */ "keyhintlabel_for_arabic_diacritics", + /* 63 */ "keylabel_for_symbols_1", + /* 64 */ "keylabel_for_symbols_2", + /* 65 */ "keylabel_for_symbols_3", + /* 66 */ "keylabel_for_symbols_4", + /* 67 */ "keylabel_for_symbols_5", + /* 68 */ "keylabel_for_symbols_6", + /* 69 */ "keylabel_for_symbols_7", + /* 70 */ "keylabel_for_symbols_8", + /* 71 */ "keylabel_for_symbols_9", + /* 72 */ "keylabel_for_symbols_0", + /* 73 */ "label_to_symbol_key", + /* 74 */ "label_to_symbol_with_microphone_key", + /* 75 */ "additional_more_keys_for_symbols_1", + /* 76 */ "additional_more_keys_for_symbols_2", + /* 77 */ "additional_more_keys_for_symbols_3", + /* 78 */ "additional_more_keys_for_symbols_4", + /* 79 */ "additional_more_keys_for_symbols_5", + /* 80 */ "additional_more_keys_for_symbols_6", + /* 81 */ "additional_more_keys_for_symbols_7", + /* 82 */ "additional_more_keys_for_symbols_8", + /* 83 */ "additional_more_keys_for_symbols_9", + /* 84 */ "additional_more_keys_for_symbols_0", + /* 85 */ "more_keys_for_symbols_1", + /* 86 */ "more_keys_for_symbols_2", + /* 87 */ "more_keys_for_symbols_3", + /* 88 */ "more_keys_for_symbols_4", + /* 89 */ "more_keys_for_symbols_5", + /* 90 */ "more_keys_for_symbols_6", + /* 91 */ "more_keys_for_symbols_7", + /* 92 */ "more_keys_for_symbols_8", + /* 93 */ "more_keys_for_symbols_9", + /* 94 */ "more_keys_for_symbols_0", + /* 95 */ "keylabel_for_comma", + /* 96 */ "more_keys_for_comma", + /* 97 */ "keylabel_for_symbols_question", + /* 98 */ "keylabel_for_symbols_semicolon", + /* 99 */ "keylabel_for_symbols_percent", + /* 100 */ "more_keys_for_symbols_exclamation", + /* 101 */ "more_keys_for_symbols_question", + /* 102 */ "more_keys_for_symbols_semicolon", + /* 103 */ "more_keys_for_symbols_percent", + /* 104 */ "keylabel_for_tablet_comma", + /* 105 */ "keyhintlabel_for_tablet_comma", + /* 106 */ "more_keys_for_tablet_comma", + /* 107 */ "keyhintlabel_for_tablet_period", + /* 108 */ "more_keys_for_tablet_period", + /* 109 */ "keylabel_for_apostrophe", + /* 110 */ "keyhintlabel_for_apostrophe", + /* 111 */ "more_keys_for_apostrophe", + /* 112 */ "more_keys_for_q", + /* 113 */ "more_keys_for_x", + /* 114 */ "keylabel_for_q", + /* 115 */ "keylabel_for_w", + /* 116 */ "keylabel_for_y", + /* 117 */ "keylabel_for_x", + /* 118 */ "keylabel_for_spanish_row2_10", + /* 119 */ "more_keys_for_am_pm", + /* 120 */ "settings_as_more_key", + /* 121 */ "shortcut_as_more_key", + /* 122 */ "action_next_as_more_key", + /* 123 */ "action_previous_as_more_key", + /* 124 */ "label_to_more_symbol_key", + /* 125 */ "label_to_more_symbol_for_tablet_key", + /* 126 */ "label_tab_key", + /* 127 */ "label_to_phone_numeric_key", + /* 128 */ "label_to_phone_symbols_key", + /* 129 */ "label_time_am", + /* 130 */ "label_time_pm", + /* 131 */ "label_to_symbol_key_pcqwerty", + /* 132 */ "keylabel_for_popular_domain", + /* 133 */ "more_keys_for_popular_domain", + /* 134 */ "more_keys_for_smiley", + /* 135 */ "single_laqm_raqm", + /* 136 */ "single_laqm_raqm_rtl", + /* 137 */ "single_raqm_laqm", + /* 138 */ "double_laqm_raqm", + /* 139 */ "double_laqm_raqm_rtl", + /* 140 */ "double_raqm_laqm", + /* 141 */ "single_lqm_rqm", + /* 142 */ "single_9qm_lqm", + /* 143 */ "single_9qm_rqm", + /* 144 */ "double_lqm_rqm", + /* 145 */ "double_9qm_lqm", + /* 146 */ "double_9qm_rqm", + /* 147 */ "more_keys_for_single_quote", + /* 148 */ "more_keys_for_double_quote", + /* 149 */ "more_keys_for_tablet_double_quote", }; private static final String EMPTY = ""; @@ -259,147 +262,147 @@ public final class KeyboardTextsSet { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - EMPTY, EMPTY, EMPTY, - /* ~41 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~44 */ // Label for "switch to alphabetic" key. - /* 42 */ "ABC", - /* 43 */ "!text/single_lqm_rqm", - /* 44 */ "!text/double_lqm_rqm", - /* 45 */ "!text/single_laqm_raqm", - /* 46 */ "!text/double_laqm_raqm", + /* 45 */ "ABC", + /* 46 */ "!text/single_lqm_rqm", + /* 47 */ "!text/double_lqm_rqm", + /* 48 */ "!text/single_laqm_raqm", + /* 49 */ "!text/double_laqm_raqm", // U+00A2: "¢" CENT SIGN // U+00A3: "£" POUND SIGN // U+20AC: "€" EURO SIGN // U+00A5: "¥" YEN SIGN // U+20B1: "₱" PESO SIGN - /* 47 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", - /* 48 */ "$", - /* 49 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", - /* 50 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", + /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + /* 51 */ "$", + /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER // U+2605: "★" BLACK STAR - /* 51 */ "\u2020,\u2021,\u2605", + /* 54 */ "\u2020,\u2021,\u2605", // U+266A: "♪" EIGHTH NOTE // U+2665: "♥" BLACK HEART SUIT // U+2660: "♠" BLACK SPADE SUIT // U+2666: "♦" BLACK DIAMOND SUIT // U+2663: "♣" BLACK CLUB SUIT - /* 52 */ "\u266A,\u2665,\u2660,\u2666,\u2663", + /* 55 */ "\u266A,\u2665,\u2660,\u2666,\u2663", // U+00B1: "±" PLUS-MINUS SIGN - /* 53 */ "\u00B1", + /* 56 */ "\u00B1", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 54 */ "!fixedColumnOrder!3,<,{,[", - /* 55 */ "!fixedColumnOrder!3,>,},]", + /* 57 */ "!fixedColumnOrder!3,<,{,[", + /* 58 */ "!fixedColumnOrder!3,>,},]", // 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 - /* 56 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", - /* 57 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", - /* 58 */ EMPTY, - /* 59 */ EMPTY, - /* 60 */ "1", - /* 61 */ "2", - /* 62 */ "3", - /* 63 */ "4", - /* 64 */ "5", - /* 65 */ "6", - /* 66 */ "7", - /* 67 */ "8", - /* 68 */ "9", - /* 69 */ "0", + /* 59 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", + /* 60 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", + /* 61 */ EMPTY, + /* 62 */ EMPTY, + /* 63 */ "1", + /* 64 */ "2", + /* 65 */ "3", + /* 66 */ "4", + /* 67 */ "5", + /* 68 */ "6", + /* 69 */ "7", + /* 70 */ "8", + /* 71 */ "9", + /* 72 */ "0", // Label for "switch to symbols" key. - /* 70 */ "?123", + /* 73 */ "?123", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "123", - /* 72~ */ + /* 74 */ "123", + /* 75~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~81 */ + /* ~84 */ // 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 - /* 82 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", + /* 85 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", // U+00B2: "²" SUPERSCRIPT TWO // U+2154: "⅔" VULGAR FRACTION TWO THIRDS - /* 83 */ "\u00B2,\u2154", + /* 86 */ "\u00B2,\u2154", // U+00B3: "³" SUPERSCRIPT THREE // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS - /* 84 */ "\u00B3,\u00BE,\u215C", + /* 87 */ "\u00B3,\u00BE,\u215C", // U+2074: "⁴" SUPERSCRIPT FOUR - /* 85 */ "\u2074", + /* 88 */ "\u2074", // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS - /* 86 */ "\u215D", - /* 87 */ EMPTY, - // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS - /* 88 */ "\u215E", - /* 89 */ EMPTY, + /* 89 */ "\u215D", /* 90 */ EMPTY, + // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS + /* 91 */ "\u215E", + /* 92 */ EMPTY, + /* 93 */ EMPTY, // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N // U+2205: "∅" EMPTY SET - /* 91 */ "\u207F,\u2205", - /* 92 */ ",", - /* 93 */ EMPTY, - /* 94 */ "?", - /* 95 */ ";", - /* 96 */ "%", + /* 94 */ "\u207F,\u2205", + /* 95 */ ",", + /* 96 */ EMPTY, + /* 97 */ "?", + /* 98 */ ";", + /* 99 */ "%", // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 97 */ "\u00A1", + /* 100 */ "\u00A1", // U+00BF: "¿" INVERTED QUESTION MARK - /* 98 */ "\u00BF", - /* 99 */ EMPTY, + /* 101 */ "\u00BF", + /* 102 */ EMPTY, // U+2030: "‰" PER MILLE SIGN - /* 100 */ "\u2030", - /* 101 */ ",", - /* 102 */ "!", - /* 103 */ "!", - /* 104 */ "?", - /* 105 */ "?", - /* 106 */ "\'", - /* 107 */ "\"", - /* 108 */ "\"", - /* 109 */ EMPTY, - /* 110 */ EMPTY, - /* 111 */ "q", - /* 112 */ "w", - /* 113 */ "y", - /* 114 */ "x", + /* 103 */ "\u2030", + /* 104 */ ",", + /* 105 */ "!", + /* 106 */ "!", + /* 107 */ "?", + /* 108 */ "?", + /* 109 */ "\'", + /* 110 */ "\"", + /* 111 */ "\"", + /* 112 */ EMPTY, + /* 113 */ EMPTY, + /* 114 */ "q", + /* 115 */ "w", + /* 116 */ "y", + /* 117 */ "x", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE - /* 115 */ "\u00F1", - /* 116 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", - /* 117 */ "!icon/settings_key|!code/key_settings", - /* 118 */ "!icon/shortcut_key|!code/key_shortcut", - /* 119 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", - /* 120 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", + /* 118 */ "\u00F1", + /* 119 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", + /* 120 */ "!icon/settings_key|!code/key_settings", + /* 121 */ "!icon/shortcut_key|!code/key_shortcut", + /* 122 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", + /* 123 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", // Label for "switch to more symbol" modifier key. Must be short to fit on key! - /* 121 */ "= \\ <", + /* 124 */ "= \\ <", // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! - /* 122 */ "~ \\ {", + /* 125 */ "~ \\ {", // Label for "Tab" key. Must be short to fit on key! - /* 123 */ "Tab", + /* 126 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! - /* 124 */ "123", + /* 127 */ "123", // Label for "switch to phone symbols" key. Must be short to fit on key! // U+FF0A: "*" FULLWIDTH ASTERISK // U+FF03: "#" FULLWIDTH NUMBER SIGN - /* 125 */ "\uFF0A\uFF03", + /* 128 */ "\uFF0A\uFF03", // Key label for "ante meridiem" - /* 126 */ "AM", + /* 129 */ "AM", // Key label for "post meridiem" - /* 127 */ "PM", + /* 130 */ "PM", // Label for "switch to symbols" key on PC QWERTY layout - /* 128 */ "Sym", - /* 129 */ ".com", + /* 131 */ "Sym", + /* 132 */ ".com", // popular web domains for the locale - most popular, displayed on the keyboard - /* 130 */ "!hasLabels!,.net,.org,.gov,.edu", - /* 131 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + /* 133 */ "!hasLabels!,.net,.org,.gov,.edu", + /* 134 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -421,24 +424,24 @@ public final class KeyboardTextsSet { // 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>. - /* 132 */ "\u2039,\u203A", - /* 133 */ "\u2039|\u203A,\u203A|\u2039", - /* 134 */ "\u203A,\u2039", - /* 135 */ "\u00AB,\u00BB", - /* 136 */ "\u00AB|\u00BB,\u00BB|\u00AB", - /* 137 */ "\u00BB,\u00AB", + /* 135 */ "\u2039,\u203A", + /* 136 */ "\u2039|\u203A,\u203A|\u2039", + /* 137 */ "\u203A,\u2039", + /* 138 */ "\u00AB,\u00BB", + /* 139 */ "\u00AB|\u00BB,\u00BB|\u00AB", + /* 140 */ "\u00BB,\u00AB", // 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>. - /* 138 */ "\u201A,\u2018,\u2019", - /* 139 */ "\u2019,\u201A,\u2018", - /* 140 */ "\u2018,\u201A,\u2019", - /* 141 */ "\u201E,\u201C,\u201D", - /* 142 */ "\u201D,\u201E,\u201C", - /* 143 */ "\u201C,\u201E,\u201D", - /* 144 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", - /* 145 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", - /* 146 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", + /* 141 */ "\u201A,\u2018,\u2019", + /* 142 */ "\u2019,\u201A,\u2018", + /* 143 */ "\u2018,\u201A,\u2019", + /* 144 */ "\u201E,\u201C,\u201D", + /* 145 */ "\u201D,\u201E,\u201C", + /* 146 */ "\u201C,\u201E,\u201D", + /* 147 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", + /* 148 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", + /* 149 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", }; /* Language af: Afrikaans */ @@ -499,45 +502,45 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0623: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "پ" ARABIC LETTER PEH - /* 42 */ "\u0623\u200C\u0628\u200C\u062C", - /* 43 */ null, - /* 44 */ null, - /* 45 */ "!text/single_laqm_raqm_rtl", - /* 46 */ "!text/double_laqm_raqm_rtl", - /* 47~ */ + /* 45 */ "\u0623\u200C\u0628\u200C\u062C", + /* 46 */ null, + /* 47 */ null, + /* 48 */ "!text/single_laqm_raqm_rtl", + /* 49 */ "!text/double_laqm_raqm_rtl", + /* 50~ */ null, null, null, - /* ~49 */ + /* ~52 */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 50 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 51 */ "\u2605,\u066D", + /* 54 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 52 */ "\u266A", - /* 53 */ null, + /* 55 */ "\u266A", + /* 56 */ null, // 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 - /* 54 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 55 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // 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 - /* 56 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 57 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0654: "ٔ" ARABIC HAMZA ABOVE // U+0652: "ْ" ARABIC SUKUN @@ -554,70 +557,115 @@ public final class KeyboardTextsSet { // 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. - /* 58 */ "!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", - /* 59 */ "\u0651", + /* 61 */ "!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", + /* 62 */ "\u0651", // U+0661: "١" ARABIC-INDIC DIGIT ONE - /* 60 */ "\u0661", + /* 63 */ "\u0661", // U+0662: "٢" ARABIC-INDIC DIGIT TWO - /* 61 */ "\u0662", + /* 64 */ "\u0662", // U+0663: "٣" ARABIC-INDIC DIGIT THREE - /* 62 */ "\u0663", + /* 65 */ "\u0663", // U+0664: "٤" ARABIC-INDIC DIGIT FOUR - /* 63 */ "\u0664", + /* 66 */ "\u0664", // U+0665: "٥" ARABIC-INDIC DIGIT FIVE - /* 64 */ "\u0665", + /* 67 */ "\u0665", // U+0666: "٦" ARABIC-INDIC DIGIT SIX - /* 65 */ "\u0666", + /* 68 */ "\u0666", // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN - /* 66 */ "\u0667", + /* 69 */ "\u0667", // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT - /* 67 */ "\u0668", + /* 70 */ "\u0668", // U+0669: "٩" ARABIC-INDIC DIGIT NINE - /* 68 */ "\u0669", + /* 71 */ "\u0669", // U+0660: "٠" ARABIC-INDIC DIGIT ZERO - /* 69 */ "\u0660", + /* 72 */ "\u0660", // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK - /* 70 */ "\u0663\u0662\u0661\u061F", + /* 73 */ "\u0663\u0662\u0661\u061F", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "\u0663\u0662\u0661", - /* 72 */ "1", - /* 73 */ "2", - /* 74 */ "3", - /* 75 */ "4", - /* 76 */ "5", - /* 77 */ "6", - /* 78 */ "7", - /* 79 */ "8", - /* 80 */ "9", + /* 74 */ "\u0663\u0662\u0661", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 81 */ "0,\u066B,\u066C", - /* 82~ */ + /* 84 */ "0,\u066B,\u066C", + /* 85~ */ null, null, null, null, null, null, null, null, null, null, - /* ~91 */ + /* ~94 */ // U+060C: "،" ARABIC COMMA - /* 92 */ "\u060C", - /* 93 */ "\\,", - /* 94 */ "\u061F", - /* 95 */ "\u061B", + /* 95 */ "\u060C", + /* 96 */ "\\,", + /* 97 */ "\u061F", + /* 98 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 96 */ "\u066A", - /* 97 */ null, - /* 98 */ "?", - /* 99 */ ";", + /* 99 */ "\u066A", + /* 100 */ null, + /* 101 */ "?", + /* 102 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 100 */ "\\%,\u2030", - /* 101~ */ + /* 103 */ "\\%,\u2030", + /* 104~ */ null, null, null, null, null, - /* ~105 */ + /* ~108 */ // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK - /* 106 */ "\u060C", - /* 107 */ "\u061F", - /* 108 */ "\u061F,\u061B,!,:,-,/,\',\"", + /* 109 */ "\u060C", + /* 110 */ "\u061F", + /* 111 */ "\u061F,\u061B,!,:,-,/,\',\"", + }; + + /* Language az: Azerbaijani */ + private static final String[] LANGUAGE_az = { + /* 0 */ null, + // U+0259: "ə" LATIN SMALL LETTER SCHWA + /* 1 */ "\u0259", + // 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 + /* 2 */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B", + // 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 + /* 3 */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D", + // 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 + /* 4 */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + // 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 + /* 5 */ "\u015F,\u00DF,\u015B,\u0161", + /* 6 */ 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 + /* 7 */ "\u00E7,\u0107,\u010D", + /* 8~ */ + null, null, null, null, null, null, null, + /* ~14 */ + // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE + /* 15 */ "\u011F", }; /* Language be: Belarusian */ @@ -637,23 +685,23 @@ public final class KeyboardTextsSet { // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I /* 29 */ "\u0456", /* 30~ */ - null, null, null, null, null, - /* ~34 */ + null, null, null, null, null, null, null, + /* ~36 */ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, - /* ~39 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, + /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 40 */ "\u0451", - /* 41 */ null, + /* 43 */ "\u0451", + /* 44 */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language bg: Bulgarian */ @@ -661,16 +709,16 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ null, + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ null, // single_quotes of Bulgarian is default single_quotes_right_left. - /* 44 */ "!text/double_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language ca: Catalan */ @@ -804,11 +852,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language da: Danish */ @@ -872,12 +921,12 @@ public final class KeyboardTextsSet { /* 24 */ "\u00F6", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language de: German */ @@ -923,12 +972,12 @@ public final class KeyboardTextsSet { /* 7~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language el: Greek */ @@ -936,13 +985,13 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA // U+0393: "Γ" GREEK CAPITAL LETTER GAMMA - /* 42 */ "\u0391\u0392\u0393", + /* 45 */ "\u0391\u0392\u0393", }; /* Language en: English */ @@ -1113,20 +1162,21 @@ public final class KeyboardTextsSet { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~108 */ - /* 109 */ "q", - /* 110 */ "x", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, + /* ~111 */ + /* 112 */ "q", + /* 113 */ "x", // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX - /* 111 */ "\u015D", + /* 114 */ "\u015D", // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX - /* 112 */ "\u011D", + /* 115 */ "\u011D", // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE - /* 113 */ "\u016D", + /* 116 */ "\u016D", // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX - /* 114 */ "\u0109", + /* 117 */ "\u0109", // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX - /* 115 */ "\u0135", + /* 118 */ "\u0135", }; /* Language es: Spanish */ @@ -1184,25 +1234,25 @@ public final class KeyboardTextsSet { /* 8~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~49 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~52 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK - /* 50 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", - /* 51~ */ + /* 53 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", + /* 54~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~102 */ + /* ~105 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 103 */ "!,\u00A1", - /* 104 */ null, + /* 106 */ "!,\u00A1", + /* 107 */ null, // U+00BF: "¿" INVERTED QUESTION MARK - /* 105 */ "?,\u00BF", - /* 106 */ "\"", - /* 107 */ "\'", - /* 108 */ "\'", + /* 108 */ "?,\u00BF", + /* 109 */ "\"", + /* 110 */ "\'", + /* 111 */ "\'", }; /* Language et: Estonian */ @@ -1305,10 +1355,10 @@ public final class KeyboardTextsSet { /* 23 */ "\u00F5", /* 24~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language fa: Persian */ @@ -1316,45 +1366,45 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // 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 - /* 42 */ "\u0627\u200C\u0628\u200C\u067E", - /* 43 */ null, - /* 44 */ null, - /* 45 */ "!text/single_laqm_raqm_rtl", - /* 46 */ "!text/double_laqm_raqm_rtl", - /* 47~ */ + /* 45 */ "\u0627\u200C\u0628\u200C\u067E", + /* 46 */ null, + /* 47 */ null, + /* 48 */ "!text/single_laqm_raqm_rtl", + /* 49 */ "!text/double_laqm_raqm_rtl", + /* 50~ */ null, null, null, - /* ~49 */ + /* ~52 */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 50 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 51 */ "\u2605,\u066D", + /* 54 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 52 */ "\u266A", - /* 53 */ null, + /* 55 */ "\u266A", + /* 56 */ null, // 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 - /* 54 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 55 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // 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 - /* 56 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", - /* 57 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", + /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", + /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0652: "ْ" ARABIC SUKUN // U+0651: "ّ" ARABIC SHADDA @@ -1371,74 +1421,74 @@ public final class KeyboardTextsSet { // 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. - /* 58 */ "!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", - /* 59 */ "\u064B", + /* 61 */ "!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", + /* 62 */ "\u064B", // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE - /* 60 */ "\u06F1", + /* 63 */ "\u06F1", // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO - /* 61 */ "\u06F2", + /* 64 */ "\u06F2", // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE - /* 62 */ "\u06F3", + /* 65 */ "\u06F3", // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR - /* 63 */ "\u06F4", + /* 66 */ "\u06F4", // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE - /* 64 */ "\u06F5", + /* 67 */ "\u06F5", // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX - /* 65 */ "\u06F6", + /* 68 */ "\u06F6", // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN - /* 66 */ "\u06F7", + /* 69 */ "\u06F7", // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT - /* 67 */ "\u06F8", + /* 70 */ "\u06F8", // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE - /* 68 */ "\u06F9", + /* 71 */ "\u06F9", // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO - /* 69 */ "\u06F0", + /* 72 */ "\u06F0", // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK - /* 70 */ "\u06F3\u06F2\u06F1\u061F", + /* 73 */ "\u06F3\u06F2\u06F1\u061F", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "\u06F3\u06F2\u06F1", - /* 72 */ "1", - /* 73 */ "2", - /* 74 */ "3", - /* 75 */ "4", - /* 76 */ "5", - /* 77 */ "6", - /* 78 */ "7", - /* 79 */ "8", - /* 80 */ "9", + /* 74 */ "\u06F3\u06F2\u06F1", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 81 */ "0,\u066B,\u066C", - /* 82~ */ + /* 84 */ "0,\u066B,\u066C", + /* 85~ */ null, null, null, null, null, null, null, null, null, null, - /* ~91 */ + /* ~94 */ // U+060C: "،" ARABIC COMMA - /* 92 */ "\u060C", - /* 93 */ "\\,", - /* 94 */ "\u061F", - /* 95 */ "\u061B", + /* 95 */ "\u060C", + /* 96 */ "\\,", + /* 97 */ "\u061F", + /* 98 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 96 */ "\u066A", - /* 97 */ null, - /* 98 */ "?", - /* 99 */ ";", + /* 99 */ "\u066A", + /* 100 */ null, + /* 101 */ "?", + /* 102 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 100 */ "\\%,\u2030", + /* 103 */ "\\%,\u2030", // 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 - /* 101 */ "\u060C", - /* 102 */ "!", - /* 103 */ "!,\\,", - /* 104 */ "\u061F", - /* 105 */ "\u061F,?", - /* 106 */ "\u060C", + /* 104 */ "\u060C", + /* 105 */ "!", + /* 106 */ "!,\\,", /* 107 */ "\u061F", - /* 108 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", + /* 108 */ "\u061F,?", + /* 109 */ "\u060C", + /* 110 */ "\u061F", + /* 111 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", }; /* Language fi: Finnish */ @@ -1546,56 +1596,56 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA - /* 42 */ "\u0915\u0916\u0917", - /* 43~ */ + /* 45 */ "\u0915\u0916\u0917", + /* 46~ */ null, null, null, null, null, - /* ~47 */ + /* ~50 */ // U+20B9: "₹" INDIAN RUPEE SIGN - /* 48 */ "\u20B9", - /* 49~ */ + /* 51 */ "\u20B9", + /* 52~ */ null, null, null, null, null, null, null, null, null, null, null, - /* ~59 */ + /* ~62 */ // U+0967: "१" DEVANAGARI DIGIT ONE - /* 60 */ "\u0967", + /* 63 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO - /* 61 */ "\u0968", + /* 64 */ "\u0968", // U+0969: "३" DEVANAGARI DIGIT THREE - /* 62 */ "\u0969", + /* 65 */ "\u0969", // U+096A: "४" DEVANAGARI DIGIT FOUR - /* 63 */ "\u096A", + /* 66 */ "\u096A", // U+096B: "५" DEVANAGARI DIGIT FIVE - /* 64 */ "\u096B", + /* 67 */ "\u096B", // U+096C: "६" DEVANAGARI DIGIT SIX - /* 65 */ "\u096C", + /* 68 */ "\u096C", // U+096D: "७" DEVANAGARI DIGIT SEVEN - /* 66 */ "\u096D", + /* 69 */ "\u096D", // U+096E: "८" DEVANAGARI DIGIT EIGHT - /* 67 */ "\u096E", + /* 70 */ "\u096E", // U+096F: "९" DEVANAGARI DIGIT NINE - /* 68 */ "\u096F", + /* 71 */ "\u096F", // U+0966: "०" DEVANAGARI DIGIT ZERO - /* 69 */ "\u0966", + /* 72 */ "\u0966", // Label for "switch to symbols" key. - /* 70 */ "?\u0967\u0968\u0969", + /* 73 */ "?\u0967\u0968\u0969", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 71 */ "\u0967\u0968\u0969", - /* 72 */ "1", - /* 73 */ "2", - /* 74 */ "3", - /* 75 */ "4", - /* 76 */ "5", - /* 77 */ "6", - /* 78 */ "7", - /* 79 */ "8", - /* 80 */ "9", - /* 81 */ "0", + /* 74 */ "\u0967\u0968\u0969", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", + /* 84 */ "0", }; /* Language hr: Croatian */ @@ -1626,11 +1676,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language hu: Hungarian */ @@ -1679,12 +1730,12 @@ public final class KeyboardTextsSet { /* 5~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language is: Icelandic */ @@ -1750,10 +1801,10 @@ public final class KeyboardTextsSet { /* 22 */ "\u00FE", /* 23~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language it: Italian */ @@ -1806,13 +1857,13 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL - /* 42 */ "\u05D0\u05D1\u05D2", + /* 45 */ "\u05D0\u05D1\u05D2", // The following characters don't need BIDI mirroring. // U+2018: "‘" LEFT SINGLE QUOTATION MARK // U+2019: "’" RIGHT SINGLE QUOTATION MARK @@ -1820,31 +1871,31 @@ public final class KeyboardTextsSet { // U+201C: "“" LEFT DOUBLE QUOTATION MARK // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK - /* 43 */ "\u2018,\u2019,\u201A", - /* 44 */ "\u201C,\u201D,\u201E", - /* 45 */ "!text/single_laqm_raqm_rtl", - /* 46 */ "!text/double_laqm_raqm_rtl", - /* 47~ */ + /* 46 */ "\u2018,\u2019,\u201A", + /* 47 */ "\u201C,\u201D,\u201E", + /* 48 */ "!text/single_laqm_raqm_rtl", + /* 49 */ "!text/double_laqm_raqm_rtl", + /* 50~ */ null, null, null, null, - /* ~50 */ + /* ~53 */ // U+2605: "★" BLACK STAR - /* 51 */ "\u2605", - /* 52 */ null, + /* 54 */ "\u2605", + /* 55 */ null, // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN - /* 53 */ "\u00B1,\uFB29", + /* 56 */ "\u00B1,\uFB29", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 54 */ "!fixedColumnOrder!3,<|>,{|},[|]", - /* 55 */ "!fixedColumnOrder!3,>|<,}|{,]|[", + /* 57 */ "!fixedColumnOrder!3,<|>,{|},[|]", + /* 58 */ "!fixedColumnOrder!3,>|<,}|{,]|[", // 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 - /* 56 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 57 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", }; /* Language ka: Georgian */ @@ -1852,15 +1903,63 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN - /* 42 */ "\u10D0\u10D1\u10D2", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u10D0\u10D1\u10D2", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + }; + + /* Language kk: Kazakh */ + private static final String[] LANGUAGE_kk = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + /* ~24 */ + // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA + /* 25 */ "\u0449", + // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN + /* 26 */ "\u044A", + // U+044B: "ы" CYRILLIC SMALL LETTER YERU + /* 27 */ "\u044B", + // U+044D: "э" CYRILLIC SMALL LETTER E + /* 28 */ "\u044D", + // U+0438: "и" CYRILLIC SMALL LETTER I + /* 29 */ "\u0438", + // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U + // U+04B1: "ұ" CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE + /* 30 */ "\u04AF,\u04B1", + // U+049B: "қ" CYRILLIC SMALL LETTER KA WITH DESCENDER + /* 31 */ "\u049B", + // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER + /* 32 */ "\u04A3", + // U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE + /* 33 */ "\u0493", + // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + /* 34 */ "\u0456", + // U+04D9: "ә" CYRILLIC SMALL LETTER SCHWA + /* 35 */ "\u04D9", + // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O + /* 36 */ "\u04E9", + // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN + /* 37 */ "\u044A", + // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA + /* 38 */ "\u04BB", + /* 39~ */ + null, null, null, null, + /* ~42 */ + // U+0451: "ё" CYRILLIC SMALL LETTER IO + /* 43 */ "\u0451", + /* 44 */ null, + // Label for "switch to alphabetic" key. + // U+0410: "А" CYRILLIC CAPITAL LETTER A + // U+0411: "Б" CYRILLIC CAPITAL LETTER BE + // U+0412: "В" CYRILLIC CAPITAL LETTER VE + /* 45 */ "\u0410\u0411\u0412", }; /* Language ky: Kirghiz */ @@ -1881,25 +1980,27 @@ public final class KeyboardTextsSet { /* 29 */ "\u0438", // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U /* 30 */ "\u04AF", + /* 31 */ null, // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER - /* 31 */ "\u04A3", - /* 32 */ null, - /* 33 */ null, + /* 32 */ "\u04A3", + /* 33~ */ + null, null, null, + /* ~35 */ // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O - /* 34 */ "\u04E9", + /* 36 */ "\u04E9", // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, - /* ~39 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, + /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 40 */ "\u0451", - /* 41 */ null, + /* 43 */ "\u0451", + /* 44 */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", + /* 45 */ "\u0410\u0411\u0412", }; /* Language lt: Lithuanian */ @@ -1992,10 +2093,10 @@ public final class KeyboardTextsSet { /* 15 */ "\u0123,\u011F", /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language lv: Latvian */ @@ -2087,10 +2188,10 @@ public final class KeyboardTextsSet { /* 15 */ "\u0123,\u011F", /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language mk: Macedonian */ @@ -2098,27 +2199,27 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~35 */ + null, null, null, null, null, null, null, null, null, + /* ~38 */ // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE - /* 36 */ "\u0455", + /* 39 */ "\u0455", // U+045C: "ќ" CYRILLIC SMALL LETTER KJE - /* 37 */ "\u045C", + /* 40 */ "\u045C", // U+0437: "з" CYRILLIC SMALL LETTER ZE - /* 38 */ "\u0437", + /* 41 */ "\u0437", // U+0453: "ѓ" CYRILLIC SMALL LETTER GJE - /* 39 */ "\u0453", + /* 42 */ "\u0453", // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE - /* 40 */ "\u0450", + /* 43 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE - /* 41 */ "\u045D", + /* 44 */ "\u045D", // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language mn: Mongolian */ @@ -2126,18 +2227,18 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43~ */ + /* 45 */ "\u0410\u0411\u0412", + /* 46~ */ null, null, null, null, null, - /* ~47 */ + /* ~50 */ // U+20AE: "₮" TUGRIK SIGN - /* 48 */ "\u20AE", + /* 51 */ "\u20AE", }; /* Language nb: Norwegian Bokmål */ @@ -2187,10 +2288,10 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E4", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language nl: Dutch */ @@ -2245,10 +2346,10 @@ public final class KeyboardTextsSet { /* 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, null, null, null, - null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language pl: Polish */ @@ -2305,10 +2406,11 @@ public final class KeyboardTextsSet { /* 14 */ "\u0142", /* 15~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language pt: Portuguese */ @@ -2411,10 +2513,10 @@ public final class KeyboardTextsSet { /* 12~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~42 */ - /* 43 */ "!text/single_9qm_rqm", - /* 44 */ "!text/double_9qm_rqm", + null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_rqm", + /* 47 */ "!text/double_9qm_rqm", }; /* Language ru: Russian */ @@ -2434,23 +2536,23 @@ public final class KeyboardTextsSet { // U+0438: "и" CYRILLIC SMALL LETTER I /* 29 */ "\u0438", /* 30~ */ - null, null, null, null, null, - /* ~34 */ + null, null, null, null, null, null, null, + /* ~36 */ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, - /* ~39 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, + /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* 40 */ "\u0451", - /* 41 */ null, + /* 43 */ "\u0451", + /* 44 */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", }; /* Language sk: Slovak */ @@ -2543,12 +2645,12 @@ public final class KeyboardTextsSet { /* 15 */ "\u0123,\u011F", /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sl: Slovenian */ @@ -2572,11 +2674,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~42 */ - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, + /* ~45 */ + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sr: Serbian */ @@ -2584,8 +2687,8 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~35 */ + null, null, null, null, null, null, null, null, null, + /* ~38 */ // 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 @@ -2605,27 +2708,27 @@ public final class KeyboardTextsSet { // END: More keys definitions for Serbian (Latin) // BEGIN: More keys definitions for Serbian (Cyrillic) // U+0437: "з" CYRILLIC SMALL LETTER ZE - /* 36 */ "\u0437", + /* 39 */ "\u0437", // U+045B: "ћ" CYRILLIC SMALL LETTER TSHE - /* 37 */ "\u045B", + /* 40 */ "\u045B", // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE - /* 38 */ "\u0455", + /* 41 */ "\u0455", // U+0452: "ђ" CYRILLIC SMALL LETTER DJE - /* 39 */ "\u0452", + /* 42 */ "\u0452", // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE - /* 40 */ "\u0450", + /* 43 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE - /* 41 */ "\u045D", + /* 44 */ "\u045D", // 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 - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sv: Swedish */ @@ -2670,10 +2773,10 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E6", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, - /* ~44 */ - /* 45 */ "!text/single_raqm_laqm", - /* 46 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, + /* ~47 */ + /* 48 */ "!text/single_raqm_laqm", + /* 49 */ "!text/double_raqm_laqm", }; /* Language sw: Swahili */ @@ -2732,18 +2835,18 @@ public final class KeyboardTextsSet { /* 0~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, - /* ~41 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI - /* 42 */ "\u0E01\u0E02\u0E04", - /* 43~ */ + /* 45 */ "\u0E01\u0E02\u0E04", + /* 46~ */ null, null, null, null, null, - /* ~47 */ + /* ~50 */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT - /* 48 */ "\u0E3F", + /* 51 */ "\u0E3F", }; /* Language tl: Tagalog */ @@ -2861,30 +2964,32 @@ public final class KeyboardTextsSet { /* 28 */ "\u0454", // U+0438: "и" CYRILLIC SMALL LETTER I /* 29 */ "\u0438", - /* 30 */ null, - /* 31 */ null, + /* 30~ */ + null, null, null, + /* ~32 */ // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN - /* 32 */ "\u0491", + /* 33 */ "\u0491", // U+0457: "ї" CYRILLIC SMALL LETTER YI - /* 33 */ "\u0457", - /* 34 */ null, + /* 34 */ "\u0457", + /* 35 */ null, + /* 36 */ null, // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* 35 */ "\u044A", - /* 36~ */ - null, null, null, null, null, null, - /* ~41 */ + /* 37 */ "\u044A", + /* 38~ */ + null, null, null, null, null, null, null, + /* ~44 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 42 */ "\u0410\u0411\u0412", - /* 43 */ "!text/single_9qm_lqm", - /* 44 */ "!text/double_9qm_lqm", - /* 45~ */ + /* 45 */ "\u0410\u0411\u0412", + /* 46 */ "!text/single_9qm_lqm", + /* 47 */ "!text/double_9qm_lqm", + /* 48~ */ null, null, null, - /* ~47 */ + /* ~50 */ // U+20B4: "₴" HRYVNIA SIGN - /* 48 */ "\u20B4", + /* 51 */ "\u20B4", }; /* Language vi: Vietnamese */ @@ -2969,10 +3074,10 @@ public final class KeyboardTextsSet { /* 10~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~47 */ + null, null, null, null, null, null, null, null, null, null, null, + /* ~50 */ // U+20AB: "₫" DONG SIGN - /* 48 */ "\u20AB", + /* 51 */ "\u20AB", }; /* Language zu: Zulu */ @@ -3149,6 +3254,7 @@ public final class KeyboardTextsSet { "DEFAULT", LANGUAGE_DEFAULT, /* default */ "af", LANGUAGE_af, /* Afrikaans */ "ar", LANGUAGE_ar, /* Arabic */ + "az", LANGUAGE_az, /* Azerbaijani */ "be", LANGUAGE_be, /* Belarusian */ "bg", LANGUAGE_bg, /* Bulgarian */ "ca", LANGUAGE_ca, /* Catalan */ @@ -3170,6 +3276,7 @@ public final class KeyboardTextsSet { "it", LANGUAGE_it, /* Italian */ "iw", LANGUAGE_iw, /* Hebrew */ "ka", LANGUAGE_ka, /* Georgian */ + "kk", LANGUAGE_kk, /* Kazakh */ "ky", LANGUAGE_ky, /* Kirghiz */ "lt", LANGUAGE_lt, /* Lithuanian */ "lv", LANGUAGE_lv, /* Latvian */ diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java new file mode 100644 index 000000000..ebbcedc96 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java @@ -0,0 +1,78 @@ +/* + * 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.Log; + +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +// TODO: Quit extending Dictionary after implementing dynamic binary dictionary. +abstract public class AbstractDictionaryWriter extends Dictionary { + /** Used for Log actions from this class */ + private static final String TAG = AbstractDictionaryWriter.class.getSimpleName(); + + private final Context mContext; + + public AbstractDictionaryWriter(final Context context, final String dictType) { + super(dictType); + mContext = context; + } + + abstract public void clear(); + + abstract public void addUnigramWord(final String word, final String shortcutTarget, + final int frequency, final boolean isNotAWord); + + abstract public void addBigramWords(final String word0, final String word1, + final int frequency, final boolean isValid); + + abstract public void removeBigramWords(final String word0, final String word1); + + abstract protected void writeBinaryDictionary(final FileOutputStream out) + throws IOException, UnsupportedFormatException; + + public void write(final String fileName) { + final String tempFileName = fileName + ".temp"; + final File file = new File(mContext.getFilesDir(), fileName); + final File tempFile = new File(mContext.getFilesDir(), tempFileName); + FileOutputStream out = null; + try { + out = new FileOutputStream(tempFile); + writeBinaryDictionary(out); + out.flush(); + out.close(); + tempFile.renameTo(file); + } catch (IOException e) { + Log.e(TAG, "IO exception while writing file", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "Unsupported format", e); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 4b5d02716..6e26a587f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -308,6 +308,7 @@ public final class BinaryDictionaryFileDumper { Log.e(TAG, "Could not have the dictionary pack delete a word list"); } BinaryDictionaryGetter.removeFilesWithIdExcept(context, wordlistId, finalFile); + Log.e(TAG, "Successfully copied file for wordlist ID " + wordlistId); // Success! Close files (through the finally{} clause) and return. return; } catch (Exception e) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 51dc85295..31a892e19 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -224,14 +224,10 @@ final public class BinaryDictionaryGetter { } } - // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason - // for this is, since those do not include whitelist entries, the new code with an old version - // of the dictionary would lose whitelist functionality. + // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since + // those do not include whitelist entries, the new code with an old version of the dictionary + // would lose whitelist functionality. private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) { - // Only for English - other languages didn't have a whitelist, hence this - // ad-hoc ## HACK ## - if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true; - FileInputStream inStream = null; try { // Read the version of the file diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index bb4a42ede..ad09b6a56 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -149,6 +149,13 @@ public final class Constants { } /** + * Custom request code used in + * {@link com.android.inputmethod.keyboard.KeyboardActionListener#onCustomRequest(int)}. + */ + // The code to show input method picker. + public static final int CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER = 1; + + /** * Some common keys code. Must be positive. */ public static final int CODE_ENTER = '\n'; diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 110be9db3..c99d0e2ea 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -109,7 +109,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { @Override public void loadDictionaryAsync() { - clearFusionDictionary(); loadDeviceAccountsEmailAddresses(); loadDictionaryAsyncForUri(ContactsContract.Profile.CONTENT_URI); // TODO: Switch this URL to the newer ContactsContract too @@ -236,6 +235,11 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } @Override + protected boolean needsToReloadBeforeWriting() { + return true; + } + + @Override protected boolean hasContentChanged() { final long startTime = SystemClock.uptimeMillis(); final int contactCount = getContactCount(); diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java index 01ec7f9a7..5dbc9b157 100644 --- a/java/src/com/android/inputmethod/latin/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/DebugSettings.java @@ -25,7 +25,7 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.ApplicationUtils; public final class DebugSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -118,7 +118,7 @@ public final class DebugSettings extends PreferenceFragment } boolean isDebugMode = mDebugMode.isChecked(); final String version = getResources().getString( - R.string.version_text, Utils.getVersionName(getActivity())); + R.string.version_text, ApplicationUtils.getVersionName(getActivity())); if (!isDebugMode) { mDebugMode.setTitle(version); mDebugMode.setSummary(""); diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java new file mode 100644 index 000000000..8be04c1c0 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java @@ -0,0 +1,107 @@ +/* + * 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 com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; +import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FusionDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.Node; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.utils.CollectionUtils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * An in memory dictionary for memorizing entries and writing a binary dictionary. + */ +public class DictionaryWriter extends AbstractDictionaryWriter { + // TODO: Regenerate version 3 binary dictionary. + private static final int BINARY_DICT_VERSION = 2; + private static final FormatSpec.FormatOptions FORMAT_OPTIONS = + new FormatSpec.FormatOptions(BINARY_DICT_VERSION); + + private FusionDictionary mFusionDictionary; + + public DictionaryWriter(final Context context, final String dictType) { + super(context, dictType); + clear(); + } + + @Override + public void clear() { + final HashMap<String, String> attributes = CollectionUtils.newHashMap(); + mFusionDictionary = new FusionDictionary(new Node(), + new FusionDictionary.DictionaryOptions(attributes, false, false)); + } + + /** + * Adds a word unigram to the fusion dictionary. + */ + // TODO: Create "cache dictionary" to cache fresh words for frequently updated dictionaries, + // considering performance regression. + @Override + public void addUnigramWord(final String word, final String shortcutTarget, final int frequency, + final boolean isNotAWord) { + if (shortcutTarget == null) { + mFusionDictionary.add(word, frequency, null, isNotAWord); + } else { + // TODO: Do this in the subclass, with this class taking an arraylist. + final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList(); + shortcutTargets.add(new WeightedString(shortcutTarget, frequency)); + mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord); + } + } + + @Override + public void addBigramWords(final String word0, final String word1, final int frequency, + final boolean isValid) { + mFusionDictionary.setBigram(word0, word1, frequency); + } + + @Override + public void removeBigramWords(final String word0, final String word1) { + // This class don't support removing bigram words. + } + + @Override + protected void writeBinaryDictionary(final FileOutputStream out) + throws IOException, UnsupportedFormatException { + BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS); + } + + @Override + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final String prevWord, final ProximityInfo proximityInfo, + boolean blockOffensiveWords) { + // This class doesn't support suggestion. + return null; + } + + @Override + public boolean isValidWord(String word) { + // This class doesn't support dictionary retrieval. + return false; + } +} diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 9cdb86c2d..657fc64b4 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -22,20 +22,12 @@ import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; -import com.android.inputmethod.latin.makedict.FormatSpec; -import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.Node; -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; -import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Abstract base class for an expandable dictionary that can be created and updated dynamically @@ -76,8 +68,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private BinaryDictionary mBinaryDictionary; - /** The expandable fusion dictionary used to generate the binary dictionary. */ - private FusionDictionary mFusionDictionary; + /** The in-memory dictionary used to generate the binary dictionary. */ + private AbstractDictionaryWriter mDictionaryWriter; /** * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple @@ -92,10 +84,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** Controls access to the local binary dictionary for this instance. */ private final DictionaryController mLocalDictionaryController = new DictionaryController(); - private static final int BINARY_DICT_VERSION = 1; - private static final FormatSpec.FormatOptions FORMAT_OPTIONS = - new FormatSpec.FormatOptions(BINARY_DICT_VERSION); - /** * Abstract method for loading the unigrams and bigrams of a given dictionary in a background * thread. @@ -137,7 +125,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mContext = context; mBinaryDictionary = null; mSharedDictionaryController = getSharedDictionaryController(filename); - clearFusionDictionary(); + mDictionaryWriter = new DictionaryWriter(context, dictType); } protected static String getFilenameWithLocale(final String name, final String localeStr) { @@ -150,53 +138,57 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Override public void close() { // Ensure that no other threads are accessing the local binary dictionary. - mLocalDictionaryController.lock(); + mLocalDictionaryController.writeLock().lock(); try { if (mBinaryDictionary != null) { mBinaryDictionary.close(); mBinaryDictionary = null; } + mDictionaryWriter.close(); } finally { - mLocalDictionaryController.unlock(); + mLocalDictionaryController.writeLock().unlock(); } } /** - * Clears the fusion dictionary on the Java side. Note: Does not modify the binary dictionary on - * the native side. + * Adds a word unigram to the dictionary. Used for loading a dictionary. */ - public void clearFusionDictionary() { - final HashMap<String, String> attributes = CollectionUtils.newHashMap(); - mFusionDictionary = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions(attributes, false, false)); + protected void addWord(final String word, final String shortcutTarget, + final int frequency, final boolean isNotAWord) { + mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); } /** - * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes - * are done to update the binary dictionary. + * Sets a word bigram in the dictionary. Used for loading a dictionary. */ - // TODO: Create "cache dictionary" to cache fresh words for frequently updated dictionaries, - // considering performance regression. - protected void addWord(final String word, final String shortcutTarget, final int frequency, - final boolean isNotAWord) { - if (shortcutTarget == null) { - mFusionDictionary.add(word, frequency, null, isNotAWord); - } else { - // TODO: Do this in the subclass, with this class taking an arraylist. - final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList(); - shortcutTargets.add(new WeightedString(shortcutTarget, frequency)); - mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord); + protected void setBigram(final String prevWord, final String word, final int frequency) { + mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */); + } + + /** + * Dynamically adds a word unigram to the dictionary. + */ + protected void addWordDynamically(final String word, final String shortcutTarget, + final int frequency, final boolean isNotAWord) { + mLocalDictionaryController.writeLock().lock(); + try { + mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); + } finally { + mLocalDictionaryController.writeLock().unlock(); } } /** - * Sets a word bigram in the fusion dictionary. Call updateBinaryDictionary when all changes are - * done to update the binary dictionary. + * Dynamically sets a word bigram in the dictionary. */ - // TODO: Create "cache dictionary" to cache fresh bigrams for frequently updated dictionaries, - // considering performance regression. - protected void setBigram(final String prevWord, final String word, final int frequency) { - mFusionDictionary.setBigram(prevWord, word, frequency); + protected void setBigramDynamically(final String prevWord, final String word, + final int frequency) { + mLocalDictionaryController.writeLock().lock(); + try { + mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */); + } finally { + mLocalDictionaryController.writeLock().unlock(); + } } @Override @@ -204,14 +196,29 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords) { asyncReloadDictionaryIfRequired(); - if (mLocalDictionaryController.tryLock()) { + // Write lock because getSuggestions in native updates session status. + if (mLocalDictionaryController.writeLock().tryLock()) { try { + final ArrayList<SuggestedWordInfo> inMemDictSuggestion = + mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo, + blockOffensiveWords); if (mBinaryDictionary != null) { - return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords); + final ArrayList<SuggestedWordInfo> binarySuggestion = + mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, + blockOffensiveWords); + if (inMemDictSuggestion == null) { + return binarySuggestion; + } else if (binarySuggestion == null) { + return inMemDictSuggestion; + } else { + binarySuggestion.addAll(binarySuggestion); + return binarySuggestion; + } + } else { + return inMemDictSuggestion; } } finally { - mLocalDictionaryController.unlock(); + mLocalDictionaryController.writeLock().unlock(); } } return null; @@ -224,11 +231,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } protected boolean isValidWordInner(final String word) { - if (mLocalDictionaryController.tryLock()) { + if (mLocalDictionaryController.readLock().tryLock()) { try { return isValidWordLocked(word); } finally { - mLocalDictionaryController.unlock(); + mLocalDictionaryController.readLock().unlock(); } } return false; @@ -239,22 +246,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return mBinaryDictionary.isValidWord(word); } - protected boolean isValidBigram(final String word1, final String word2) { - if (mBinaryDictionary == null) return false; - return mBinaryDictionary.isValidBigram(word1, word2); - } - - protected boolean isValidBigramInner(final String word1, final String word2) { - if (mLocalDictionaryController.tryLock()) { - try { - return isValidBigramLocked(word1, word2); - } finally { - mLocalDictionaryController.unlock(); - } - } - return false; - } - protected boolean isValidBigramLocked(final String word1, final String word2) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidBigram(word1, word2); @@ -273,7 +264,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Loads the current binary dictionary from internal storage. Assumes the dictionary file * exists. */ - protected void loadBinaryDictionary() { + private void loadBinaryDictionary() { if (DEBUG) { Log.d(TAG, "Loading binary dictionary: " + mFilename + " request=" + mSharedDictionaryController.mLastUpdateRequestTime + " update=" @@ -292,9 +283,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // Ensure all threads accessing the current dictionary have finished before swapping in // the new one. final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; - mLocalDictionaryController.lock(); - mBinaryDictionary = newBinaryDictionary; - mLocalDictionaryController.unlock(); + mLocalDictionaryController.writeLock().lock(); + try { + mBinaryDictionary = newBinaryDictionary; + } finally { + mLocalDictionaryController.writeLock().unlock(); + } oldBinaryDictionary.close(); } else { mBinaryDictionary = newBinaryDictionary; @@ -302,6 +296,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } /** + * Abstract method for checking if it is required to reload the dictionary before writing + * a binary dictionary. + */ + abstract protected boolean needsToReloadBeforeWriting(); + + /** * Generates and writes a new binary dictionary based on the contents of the fusion dictionary. */ private void generateBinaryDictionary() { @@ -310,33 +310,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { + mSharedDictionaryController.mLastUpdateRequestTime + " update=" + mSharedDictionaryController.mLastUpdateTime); } - - loadDictionaryAsync(); - - final String tempFileName = mFilename + ".temp"; - final File file = new File(mContext.getFilesDir(), mFilename); - final File tempFile = new File(mContext.getFilesDir(), tempFileName); - FileOutputStream out = null; - try { - out = new FileOutputStream(tempFile); - BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS); - out.flush(); - out.close(); - tempFile.renameTo(file); - clearFusionDictionary(); - } catch (IOException e) { - Log.e(TAG, "IO exception while writing file", e); - } catch (UnsupportedFormatException e) { - Log.e(TAG, "Unsupported format", e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // ignore - } - } + if (needsToReloadBeforeWriting()) { + mDictionaryWriter.clear(); + loadDictionaryAsync(); } + mDictionaryWriter.write(mFilename); } /** @@ -389,7 +367,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private final void syncReloadDictionaryInternal() { // Ensure that only one thread attempts to read or write to the shared binary dictionary // file at the same time. - mSharedDictionaryController.lock(); + mSharedDictionaryController.writeLock().lock(); try { final long time = SystemClock.uptimeMillis(); final boolean dictionaryFileExists = dictionaryFileExists(); @@ -415,9 +393,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // shared dictionary. loadBinaryDictionary(); } + if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) { + // Binary dictionary is not valid. Regenerate the dictionary file. + mSharedDictionaryController.mLastUpdateTime = time; + generateBinaryDictionary(); + loadBinaryDictionary(); + } mLocalDictionaryController.mLastUpdateTime = time; } finally { - mSharedDictionaryController.unlock(); + mSharedDictionaryController.writeLock().unlock(); } } @@ -442,7 +426,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * dictionary is out of date. Can be shared across multiple dictionary instances that access the * same filename. */ - private static class DictionaryController extends ReentrantLock { + private static class DictionaryController extends ReentrantReadWriteLock { private volatile long mLastUpdateTime = 0; private volatile long mLastUpdateRequestTime = 0; diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index a67c919b3..2666573bb 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -728,172 +728,206 @@ public class ExpandableDictionary extends Dictionary { * to their base characters. If c is in range, BASE_CHARS[c] == c * if c is not a combined character, or the base character if it * is combined. + * + * cf. native/jni/src/utils/char_utils.cpp */ private static final char BASE_CHARS[] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, - 0x0020, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, - 0x0020, 0x00a9, 0x0061, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0020, - 0x00b0, 0x00b1, 0x0032, 0x0033, 0x0020, 0x03bc, 0x00b6, 0x00b7, - 0x0020, 0x0031, 0x006f, 0x00bb, 0x0031, 0x0031, 0x0033, 0x00bf, - 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00c6, 0x0043, - 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, - 0x00d0, 0x004e, 0x004f, 0x004f, 0x004f, 0x004f, 0x004f, 0x00d7, - 0x004f, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00de, 0x0073, // Manually changed d8 to 4f - // Manually changed df to 73 - 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063, - 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, - 0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00f7, - 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0079, // Manually changed f8 to 6f - 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, - 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, - 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, - 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067, - 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127, - 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, - 0x0049, 0x0131, 0x0049, 0x0069, 0x004a, 0x006a, 0x004b, 0x006b, - 0x0138, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, - 0x006c, 0x0141, 0x0142, 0x004e, 0x006e, 0x004e, 0x006e, 0x004e, - 0x006e, 0x02bc, 0x014a, 0x014b, 0x004f, 0x006f, 0x004f, 0x006f, - 0x004f, 0x006f, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072, - 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, - 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167, - 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, - 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, - 0x0059, 0x005a, 0x007a, 0x005a, 0x007a, 0x005a, 0x007a, 0x0073, - 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, - 0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d, 0x018e, 0x018f, - 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, - 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f, - 0x004f, 0x006f, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7, - 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x0055, - 0x0075, 0x01b1, 0x01b2, 0x01b3, 0x01b4, 0x01b5, 0x01b6, 0x01b7, - 0x01b8, 0x01b9, 0x01ba, 0x01bb, 0x01bc, 0x01bd, 0x01be, 0x01bf, - 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x0044, 0x0044, 0x0064, 0x004c, - 0x004c, 0x006c, 0x004e, 0x004e, 0x006e, 0x0041, 0x0061, 0x0049, - 0x0069, 0x004f, 0x006f, 0x0055, 0x0075, 0x00dc, 0x00fc, 0x00dc, - 0x00fc, 0x00dc, 0x00fc, 0x00dc, 0x00fc, 0x01dd, 0x00c4, 0x00e4, - 0x0226, 0x0227, 0x00c6, 0x00e6, 0x01e4, 0x01e5, 0x0047, 0x0067, - 0x004b, 0x006b, 0x004f, 0x006f, 0x01ea, 0x01eb, 0x01b7, 0x0292, - 0x006a, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01f6, 0x01f7, - 0x004e, 0x006e, 0x00c5, 0x00e5, 0x00c6, 0x00e6, 0x00d8, 0x00f8, - 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, - 0x0049, 0x0069, 0x0049, 0x0069, 0x004f, 0x006f, 0x004f, 0x006f, - 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, - 0x0053, 0x0073, 0x0054, 0x0074, 0x021c, 0x021d, 0x0048, 0x0068, - 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061, - 0x0045, 0x0065, 0x00d6, 0x00f6, 0x00d5, 0x00f5, 0x004f, 0x006f, - 0x022e, 0x022f, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, - 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f, - 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, - 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f, - 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, - 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, - 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, - 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f, - 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, - 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, - 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, - 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, - 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, - 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, - 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, - 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, - 0x0068, 0x0266, 0x006a, 0x0072, 0x0279, 0x027b, 0x0281, 0x0077, - 0x0079, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, - 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, - 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, - 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, - 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02de, 0x02df, - 0x0263, 0x006c, 0x0073, 0x0078, 0x0295, 0x02e5, 0x02e6, 0x02e7, - 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, - 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, - 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff, - 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, - 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, - 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, - 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, - 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347, - 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, - 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, - 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, - 0x0370, 0x0371, 0x0372, 0x0373, 0x02b9, 0x0375, 0x0376, 0x0377, - 0x0378, 0x0379, 0x0020, 0x037b, 0x037c, 0x037d, 0x003b, 0x037f, - 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00a8, 0x0391, 0x00b7, - 0x0395, 0x0397, 0x0399, 0x038b, 0x039f, 0x038d, 0x03a5, 0x03a9, - 0x03ca, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x0399, 0x03a5, 0x03b1, 0x03b5, 0x03b7, 0x03b9, - 0x03cb, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, - 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, - 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, - 0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03bf, 0x03c5, 0x03c9, 0x03cf, - 0x03b2, 0x03b8, 0x03a5, 0x03d2, 0x03d2, 0x03c6, 0x03c0, 0x03d7, - 0x03d8, 0x03d9, 0x03da, 0x03db, 0x03dc, 0x03dd, 0x03de, 0x03df, - 0x03e0, 0x03e1, 0x03e2, 0x03e3, 0x03e4, 0x03e5, 0x03e6, 0x03e7, - 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03ed, 0x03ee, 0x03ef, - 0x03ba, 0x03c1, 0x03c2, 0x03f3, 0x0398, 0x03b5, 0x03f6, 0x03f7, - 0x03f8, 0x03a3, 0x03fa, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff, - 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406, - 0x0408, 0x0409, 0x040a, 0x040b, 0x041a, 0x0418, 0x0423, 0x040f, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0418, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, - 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, - 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, - 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, - 0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f, - 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, - 0x0468, 0x0469, 0x046a, 0x046b, 0x046c, 0x046d, 0x046e, 0x046f, - 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475, - 0x0478, 0x0479, 0x047a, 0x047b, 0x047c, 0x047d, 0x047e, 0x047f, - 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, - 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f, - 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, - 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x049f, - 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, - 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af, - 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, - 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x04be, 0x04bf, - 0x04c0, 0x0416, 0x0436, 0x04c3, 0x04c4, 0x04c5, 0x04c6, 0x04c7, - 0x04c8, 0x04c9, 0x04ca, 0x04cb, 0x04cc, 0x04cd, 0x04ce, 0x04cf, - 0x0410, 0x0430, 0x0410, 0x0430, 0x04d4, 0x04d5, 0x0415, 0x0435, - 0x04d8, 0x04d9, 0x04d8, 0x04d9, 0x0416, 0x0436, 0x0417, 0x0437, - 0x04e0, 0x04e1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041e, 0x043e, - 0x04e8, 0x04e9, 0x04e8, 0x04e9, 0x042d, 0x044d, 0x0423, 0x0443, - 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04f6, 0x04f7, - 0x042b, 0x044b, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff, + /* U+0000 */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + /* U+0008 */ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + /* U+0010 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + /* U+0018 */ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + /* U+0020 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + /* U+0028 */ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + /* U+0030 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + /* U+0038 */ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + /* U+0040 */ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + /* U+0048 */ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + /* U+0050 */ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + /* U+0058 */ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + /* U+0060 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + /* U+0068 */ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* U+0070 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + /* U+0078 */ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + /* U+0080 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + /* U+0088 */ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + /* U+0090 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + /* U+0098 */ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + /* U+00A0 */ 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + /* U+00A8 */ 0x0020, 0x00A9, 0x0061, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0020, + /* U+00B0 */ 0x00B0, 0x00B1, 0x0032, 0x0033, 0x0020, 0x03BC, 0x00B6, 0x00B7, + /* U+00B8 */ 0x0020, 0x0031, 0x006F, 0x00BB, 0x0031, 0x0031, 0x0033, 0x00BF, + /* U+00C0 */ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00C6, 0x0043, + /* U+00C8 */ 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, + /* U+00D0 */ 0x00D0, 0x004E, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x00D7, + /* U+00D8 */ 0x004F, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00DE, 0x0073, + // U+00D8: Manually changed from 00D8 to 004F + // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O + // U+00DF: Manually changed from 00DF to 0073 + /* U+00E0 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00E6, 0x0063, + /* U+00E8 */ 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, + /* U+00F0 */ 0x00F0, 0x006E, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x00F7, + /* U+00F8 */ 0x006F, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00FE, 0x0079, + // U+00F8: Manually changed from 00F8 to 006F + // TODO: Check if it's really acceptable to consider ø a diacritical variant of o + /* U+0100 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, + /* U+0108 */ 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, + /* U+0110 */ 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, + /* U+0118 */ 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067, + /* U+0120 */ 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127, + /* U+0128 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, + /* U+0130 */ 0x0049, 0x0131, 0x0049, 0x0069, 0x004A, 0x006A, 0x004B, 0x006B, + /* U+0138 */ 0x0138, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, + /* U+0140 */ 0x006C, 0x004C, 0x006C, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E, + // U+0141: Manually changed from 0141 to 004C + // U+0142: Manually changed from 0142 to 006C + /* U+0148 */ 0x006E, 0x02BC, 0x014A, 0x014B, 0x004F, 0x006F, 0x004F, 0x006F, + /* U+0150 */ 0x004F, 0x006F, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072, + /* U+0158 */ 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, + /* U+0160 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167, + /* U+0168 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, + /* U+0170 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, + /* U+0178 */ 0x0059, 0x005A, 0x007A, 0x005A, 0x007A, 0x005A, 0x007A, 0x0073, + /* U+0180 */ 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, + /* U+0188 */ 0x0188, 0x0189, 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, + /* U+0190 */ 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, + /* U+0198 */ 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, + /* U+01A0 */ 0x004F, 0x006F, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, + /* U+01A8 */ 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x0055, + /* U+01B0 */ 0x0075, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7, + /* U+01B8 */ 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF, + /* U+01C0 */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x0044, 0x0044, 0x0064, 0x004C, + /* U+01C8 */ 0x004C, 0x006C, 0x004E, 0x004E, 0x006E, 0x0041, 0x0061, 0x0049, + /* U+01D0 */ 0x0069, 0x004F, 0x006F, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, + // U+01D5: Manually changed from 00DC to 0055 + // U+01D6: Manually changed from 00FC to 0075 + // U+01D7: Manually changed from 00DC to 0055 + /* U+01D8 */ 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x01DD, 0x0041, 0x0061, + // U+01D8: Manually changed from 00FC to 0075 + // U+01D9: Manually changed from 00DC to 0055 + // U+01DA: Manually changed from 00FC to 0075 + // U+01DB: Manually changed from 00DC to 0055 + // U+01DC: Manually changed from 00FC to 0075 + // U+01DE: Manually changed from 00C4 to 0041 + // U+01DF: Manually changed from 00E4 to 0061 + /* U+01E0 */ 0x0041, 0x0061, 0x00C6, 0x00E6, 0x01E4, 0x01E5, 0x0047, 0x0067, + // U+01E0: Manually changed from 0226 to 0041 + // U+01E1: Manually changed from 0227 to 0061 + /* U+01E8 */ 0x004B, 0x006B, 0x004F, 0x006F, 0x004F, 0x006F, 0x01B7, 0x0292, + // U+01EC: Manually changed from 01EA to 004F + // U+01ED: Manually changed from 01EB to 006F + /* U+01F0 */ 0x006A, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01F6, 0x01F7, + /* U+01F8 */ 0x004E, 0x006E, 0x0041, 0x0061, 0x00C6, 0x00E6, 0x004F, 0x006F, + // U+01FA: Manually changed from 00C5 to 0041 + // U+01FB: Manually changed from 00E5 to 0061 + // U+01FE: Manually changed from 00D8 to 004F + // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O + // U+01FF: Manually changed from 00F8 to 006F + // TODO: Check if it's really acceptable to consider ø a diacritical variant of o + /* U+0200 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, + /* U+0208 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x004F, 0x006F, 0x004F, 0x006F, + /* U+0210 */ 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, + /* U+0218 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x021C, 0x021D, 0x0048, 0x0068, + /* U+0220 */ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061, + /* U+0228 */ 0x0045, 0x0065, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, + // U+022A: Manually changed from 00D6 to 004F + // U+022B: Manually changed from 00F6 to 006F + // U+022C: Manually changed from 00D5 to 004F + // U+022D: Manually changed from 00F5 to 006F + /* U+0230 */ 0x004F, 0x006F, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, + // U+0230: Manually changed from 022E to 004F + // U+0231: Manually changed from 022F to 006F + /* U+0238 */ 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F, + /* U+0240 */ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, + /* U+0248 */ 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, + /* U+0250 */ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, + /* U+0258 */ 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, + /* U+0260 */ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, + /* U+0268 */ 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, + /* U+0270 */ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, + /* U+0278 */ 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, + /* U+0280 */ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, + /* U+0288 */ 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F, + /* U+0290 */ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, + /* U+0298 */ 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, + /* U+02A0 */ 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, + /* U+02A8 */ 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF, + /* U+02B0 */ 0x0068, 0x0266, 0x006A, 0x0072, 0x0279, 0x027B, 0x0281, 0x0077, + /* U+02B8 */ 0x0079, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, + /* U+02C0 */ 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, + /* U+02C8 */ 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF, + /* U+02D0 */ 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, + /* U+02D8 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02DE, 0x02DF, + /* U+02E0 */ 0x0263, 0x006C, 0x0073, 0x0078, 0x0295, 0x02E5, 0x02E6, 0x02E7, + /* U+02E8 */ 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, + /* U+02F0 */ 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, + /* U+02F8 */ 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF, + /* U+0300 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + /* U+0308 */ 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + /* U+0310 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + /* U+0318 */ 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + /* U+0320 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + /* U+0328 */ 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + /* U+0330 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + /* U+0338 */ 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + /* U+0340 */ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347, + /* U+0348 */ 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + /* U+0350 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + /* U+0358 */ 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + /* U+0360 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + /* U+0368 */ 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + /* U+0370 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x02B9, 0x0375, 0x0376, 0x0377, + /* U+0378 */ 0x0378, 0x0379, 0x0020, 0x037B, 0x037C, 0x037D, 0x003B, 0x037F, + /* U+0380 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00A8, 0x0391, 0x00B7, + /* U+0388 */ 0x0395, 0x0397, 0x0399, 0x038B, 0x039F, 0x038D, 0x03A5, 0x03A9, + /* U+0390 */ 0x03CA, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + /* U+0398 */ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + /* U+03A0 */ 0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + /* U+03A8 */ 0x03A8, 0x03A9, 0x0399, 0x03A5, 0x03B1, 0x03B5, 0x03B7, 0x03B9, + /* U+03B0 */ 0x03CB, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + /* U+03B8 */ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* U+03C0 */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + /* U+03C8 */ 0x03C8, 0x03C9, 0x03B9, 0x03C5, 0x03BF, 0x03C5, 0x03C9, 0x03CF, + /* U+03D0 */ 0x03B2, 0x03B8, 0x03A5, 0x03D2, 0x03D2, 0x03C6, 0x03C0, 0x03D7, + /* U+03D8 */ 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + /* U+03E0 */ 0x03E0, 0x03E1, 0x03E2, 0x03E3, 0x03E4, 0x03E5, 0x03E6, 0x03E7, + /* U+03E8 */ 0x03E8, 0x03E9, 0x03EA, 0x03EB, 0x03EC, 0x03ED, 0x03EE, 0x03EF, + /* U+03F0 */ 0x03BA, 0x03C1, 0x03C2, 0x03F3, 0x0398, 0x03B5, 0x03F6, 0x03F7, + /* U+03F8 */ 0x03F8, 0x03A3, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + /* U+0400 */ 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406, + /* U+0408 */ 0x0408, 0x0409, 0x040A, 0x040B, 0x041A, 0x0418, 0x0423, 0x040F, + /* U+0410 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + /* U+0418 */ 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + // U+0419: Manually changed from 0418 to 0419 + /* U+0420 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + /* U+0428 */ 0x0428, 0x0429, 0x042C, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + // U+042A: Manually changed from 042A to 042C + /* U+0430 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + /* U+0438 */ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + // U+0439: Manually changed from 0438 to 0439 + /* U+0440 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + /* U+0448 */ 0x0448, 0x0449, 0x044C, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + // U+044A: Manually changed from 044A to 044C + /* U+0450 */ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, + /* U+0458 */ 0x0458, 0x0459, 0x045A, 0x045B, 0x043A, 0x0438, 0x0443, 0x045F, + /* U+0460 */ 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, + /* U+0468 */ 0x0468, 0x0469, 0x046A, 0x046B, 0x046C, 0x046D, 0x046E, 0x046F, + /* U+0470 */ 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475, + /* U+0478 */ 0x0478, 0x0479, 0x047A, 0x047B, 0x047C, 0x047D, 0x047E, 0x047F, + /* U+0480 */ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, + /* U+0488 */ 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + /* U+0490 */ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, + /* U+0498 */ 0x0498, 0x0499, 0x049A, 0x049B, 0x049C, 0x049D, 0x049E, 0x049F, + /* U+04A0 */ 0x04A0, 0x04A1, 0x04A2, 0x04A3, 0x04A4, 0x04A5, 0x04A6, 0x04A7, + /* U+04A8 */ 0x04A8, 0x04A9, 0x04AA, 0x04AB, 0x04AC, 0x04AD, 0x04AE, 0x04AF, + /* U+04B0 */ 0x04B0, 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7, + /* U+04B8 */ 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF, + /* U+04C0 */ 0x04C0, 0x0416, 0x0436, 0x04C3, 0x04C4, 0x04C5, 0x04C6, 0x04C7, + /* U+04C8 */ 0x04C8, 0x04C9, 0x04CA, 0x04CB, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + /* U+04D0 */ 0x0410, 0x0430, 0x0410, 0x0430, 0x04D4, 0x04D5, 0x0415, 0x0435, + /* U+04D8 */ 0x04D8, 0x04D9, 0x04D8, 0x04D9, 0x0416, 0x0436, 0x0417, 0x0437, + /* U+04E0 */ 0x04E0, 0x04E1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041E, 0x043E, + /* U+04E8 */ 0x04E8, 0x04E9, 0x04E8, 0x04E9, 0x042D, 0x044D, 0x0423, 0x0443, + /* U+04F0 */ 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04F6, 0x04F7, + /* U+04F8 */ 0x042B, 0x044B, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, }; - - // generated with: - // cat UnicodeData.txt | perl -e 'while (<>) { @foo = split(/;/); $foo[5] =~ s/<.*> //; $base[hex($foo[0])] = hex($foo[5]);} for ($i = 0; $i < 0x500; $i += 8) { for ($j = $i; $j < $i + 8; $j++) { printf("0x%04x, ", $base[$j] ? $base[$j] : $j)}; print "\n"; }' - } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0560cf528..8f5e57182 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -73,22 +73,22 @@ 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.RichInputConnection.Range; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.SuggestionStripView; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CompletionInfoUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; +import com.android.inputmethod.latin.utils.LatinImeLoggerUtils; import com.android.inputmethod.latin.utils.PositionalInfoForUserDictPendingAddition; import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; -import com.android.inputmethod.latin.utils.Utils; -import com.android.inputmethod.latin.utils.Utils.Stats; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.ResearchLogger; import java.io.FileDescriptor; @@ -961,7 +961,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: is it still necessary to test for composingSpan related stuff? final boolean selectionChangedOrSafeToReset = selectionChanged || (!mWordComposer.isComposingWord()) || noComposingSpan; - if (selectionChangedOrSafeToReset) { + final boolean hasOrHadSelection = (oldSelStart != oldSelEnd + || newSelStart != newSelEnd); + final int moveAmount = newSelStart - oldSelStart; + if (selectionChangedOrSafeToReset && (hasOrHadSelection + || !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 @@ -1356,14 +1360,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen showSubtypeSelectorAndSettings(); } - // Virtual codes representing custom requests. These are used in onCustomRequest() below. - public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1; - @Override public boolean onCustomRequest(final int requestCode) { if (isShowingOptionDialog()) return false; switch (requestCode) { - case CODE_SHOW_INPUT_METHOD_PICKER: + case Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER: if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { mRichImm.getInputMethodManager().showInputMethodPicker(); return true; @@ -1550,7 +1551,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (SPACE_STATE_PHANTOM == spaceState) { if (mSettings.isInternal()) { if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) { - Stats.onAutoCorrection( + LatinImeLoggerUtils.onAutoCorrection( "", mWordComposer.getTypedWord(), " ", mWordComposer); } } @@ -1611,7 +1612,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isComposingWord()) { if (mSettings.isInternal()) { if (mWordComposer.isBatchMode()) { - Stats.onAutoCorrection("", mWordComposer.getTypedWord(), " ", mWordComposer); + LatinImeLoggerUtils.onAutoCorrection( + "", mWordComposer.getTypedWord(), " ", mWordComposer); } } final int wordComposerSize = mWordComposer.size(); @@ -1859,7 +1861,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { if (mLastComposedWord.canRevertCommit()) { if (mSettings.isInternal()) { - Stats.onAutoCorrectionCancellation(); + LatinImeLoggerUtils.onAutoCorrectionCancellation(); } revertCommit(); return; @@ -2030,7 +2032,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mHandler.postUpdateSuggestionStrip(); if (mSettings.isInternal()) { - Utils.Stats.onNonSeparator((char)primaryCode, x, y); + LatinImeLoggerUtils.onNonSeparator((char)primaryCode, x, y); } } @@ -2135,7 +2137,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setPunctuationSuggestions(); } if (mSettings.isInternal()) { - Utils.Stats.onSeparator((char)primaryCode, x, y); + LatinImeLoggerUtils.onSeparator((char)primaryCode, x, y); } mKeyboardSwitcher.updateShiftState(); @@ -2324,7 +2326,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } if (mSettings.isInternal()) { - Stats.onAutoCorrection(typedWord, autoCorrection, separatorString, mWordComposer); + LatinImeLoggerUtils.onAutoCorrection( + typedWord, autoCorrection, separatorString, mWordComposer); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { final SuggestedWords suggestedWords = mSuggestedWords; @@ -2428,7 +2431,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen && !AutoCorrection.isValidWord(mSuggest, suggestion, true); if (mSettings.isInternal()) { - Stats.onSeparator((char)Constants.CODE_SPACE, + LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { @@ -2514,7 +2517,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If we don't know the cursor location, return. if (mLastSelectionStart < 0) return; if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return; - final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), + final TextRange range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), 0 /* additionalPrecedingWordsCount */); if (null == range) return; // Happens if we don't have an input connection at all // If for some strange reason (editor bug or so) we measure the text before the cursor as @@ -2535,6 +2538,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard()); + // TODO: this is in chars but the callee expects code points! mWordComposer.setCursorPositionWithinWord(numberOfCharsInWordBeforeCursor); mConnection.setComposingRegion( mLastSelectionStart - numberOfCharsInWordBeforeCursor, @@ -2631,7 +2635,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1); if (mSettings.isInternal()) { - Stats.onSeparator(mLastComposedWord.mSeparatorString, + LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -2673,15 +2677,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - // Callback called by PointerTracker through the KeyboardActionListener. This is called when a - // key is depressed; release matching call is onReleaseKey below. + // 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 boolean isSinglePointer) { mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer); + final MainKeyboardView mKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); + final boolean noFeedback = (mKeyboardView != null && mKeyboardView.isInSlidingKeyInput()) + || (primaryCode == Constants.CODE_DELETE && !mConnection.canDeleteCharacters()); + if (!noFeedback) { + AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback( + primaryCode, mKeyboardView); + } } - // Callback by PointerTracker through the KeyboardActionListener. This is called when a key - // is released; press matching call is onPressKey above. + // Callback of the {@link KeyboardActionListener}. This is called when a key is released; + // press matching call is {@link #onPressKey(int,boolean)} above. @Override public void onReleaseKey(final int primaryCode, final boolean withSliding) { mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding); @@ -2778,7 +2789,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence[] items = new CharSequence[] { // TODO: Should use new string "Select active input modes". getString(R.string.language_selection_title), - getString(Utils.getAcitivityTitleResId(this, SettingsActivity.class)), + getString(ApplicationUtils.getAcitivityTitleResId(this, SettingsActivity.class)), }; final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 6b22cb12e..461de53ad 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -17,9 +17,7 @@ package com.android.inputmethod.latin; import android.inputmethodservice.InputMethodService; -import android.text.Spanned; import android.text.TextUtils; -import android.text.style.SuggestionSpan; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -32,9 +30,9 @@ import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.ResearchLogger; -import java.util.Arrays; import java.util.Locale; import java.util.regex.Pattern; @@ -193,6 +191,10 @@ public final class RichInputConnection { return mIC.getSelectedText(flags); } + public boolean canDeleteCharacters() { + return mCurrentCursorPosition > 0; + } + /** * Gets the caps modes we should be in after this specific string. * @@ -441,100 +443,6 @@ public final class RichInputConnection { return getNthPreviousWord(prev, sentenceSeperators, n); } - /** - * Represents a range of text, relative to the current cursor position. - */ - public static final class Range { - private final CharSequence mTextAtCursor; - private final int mWordAtCursorStartIndex; - private final int mWordAtCursorEndIndex; - private final int mCursorIndex; - - public final CharSequence mWord; - - public int getNumberOfCharsInWordBeforeCursor() { - return mCursorIndex - mWordAtCursorStartIndex; - } - - public int getNumberOfCharsInWordAfterCursor() { - return mWordAtCursorEndIndex - mCursorIndex; - } - - /** - * 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, we 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 Range(final CharSequence textAtCursor, final int wordAtCursorStartIndex, - final int wordAtCursorEndIndex, final int cursorIndex) { - if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex - || cursorIndex > wordAtCursorEndIndex - || wordAtCursorEndIndex > textAtCursor.length()) { - throw new IndexOutOfBoundsException(); - } - mTextAtCursor = textAtCursor; - mWordAtCursorStartIndex = wordAtCursorStartIndex; - mWordAtCursorEndIndex = wordAtCursorEndIndex; - mCursorIndex = cursorIndex; - mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex); - } - } - private static boolean isSeparator(int code, String sep) { return sep.indexOf(code) != -1; } @@ -581,7 +489,7 @@ public final class RichInputConnection { */ public CharSequence getWordAtCursor(String separators) { // getWordRangeAtCursor returns null if the connection is null - Range r = getWordRangeAtCursor(separators, 0); + TextRange r = getWordRangeAtCursor(separators, 0); return (r == null) ? null : r.mWord; } @@ -593,7 +501,8 @@ public final class RichInputConnection { * be included in the returned range * @return a range containing the text surrounding the cursor */ - public Range getWordRangeAtCursor(final String sep, final int additionalPrecedingWordsCount) { + public TextRange getWordRangeAtCursor(final String sep, + final int additionalPrecedingWordsCount) { mIC = mParent.getCurrentInputConnection(); if (mIC == null || sep == null) { return null; @@ -643,7 +552,7 @@ public final class RichInputConnection { } } - return new Range(TextUtils.concat(before, after), startIndexInBefore, + return new TextRange(TextUtils.concat(before, after), startIndexInBefore, before.length() + endIndexInAfter, before.length()); } diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java index f52e56441..8c41cf8b9 100644 --- a/java/src/com/android/inputmethod/latin/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java @@ -42,8 +42,8 @@ import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; import com.android.inputmethod.latin.userdictionary.UserDictionaryList; import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; import com.android.inputmethod.latin.utils.AdditionalFeaturesSettingUtils; +import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.FeedbackUtils; -import com.android.inputmethod.latin.utils.Utils; import com.android.inputmethod.research.ResearchLogger; import com.android.inputmethodcommon.InputMethodSettingsFragment; @@ -90,7 +90,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment final PreferenceScreen preferenceScreen = getPreferenceScreen(); if (preferenceScreen != null) { preferenceScreen.setTitle( - Utils.getAcitivityTitleResId(getActivity(), SettingsActivity.class)); + ApplicationUtils.getAcitivityTitleResId(getActivity(), SettingsActivity.class)); } final Resources res = getResources(); diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 5b47dda0d..22beaefee 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -75,6 +75,21 @@ public final class SuggestedWords { return mSuggestedWordInfoList.get(index); } + public String getDebugString(final int pos) { + if (!LatinImeLogger.sDBG) { + 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; + } + public boolean willAutoCorrect() { return mWillAutoCorrect; } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index ba84c1ad3..ab8f34893 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -240,7 +240,6 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { private void addWords(final Cursor cursor) { final boolean hasShortcutColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - clearFusionDictionary(); if (cursor == null) return; if (cursor.moveToFirst()) { final int indexWord = cursor.getColumnIndex(Words.WORD); @@ -267,4 +266,9 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { protected boolean hasContentChanged() { return true; } + + @Override + protected boolean needsToReloadBeforeWriting() { + return true; + } } diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index ca2d8840b..8c668b810 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -320,7 +320,11 @@ public final class UserHistoryDictionary extends ExpandableDictionary { mUserHistoryDictionary.mBigramListLock.unlock(); } } else if (mUserHistoryDictionary.mBigramListLock.tryLock()) { - doWriteTaskLocked(); + try { + doWriteTaskLocked(); + } finally { + mUserHistoryDictionary.mBigramListLock.unlock(); + } } return null; } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index e078f03f4..2babe8b0c 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -192,6 +192,40 @@ public final class WordComposer { 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 actualMoveAmountWithinWord = 0; + int cursorPos = mCursorPositionWithinWord; + 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 (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) { + actualMoveAmountWithinWord += Character.charCount(mPrimaryKeyCodes[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 (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) { + --cursorPos; + actualMoveAmountWithinWord -= Character.charCount(mPrimaryKeyCodes[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 (actualMoveAmountWithinWord != expectedMoveAmount) return false; + mCursorPositionWithinWord = cursorPos; + return true; + } + public void setBatchInputPointers(final InputPointers batchPointers) { mInputPointers.set(batchPointers); mIsBatchMode = true; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java index da5812603..999ca775b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java @@ -21,7 +21,7 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.ApplicationUtils; /** * Preference screen. @@ -39,7 +39,7 @@ public final class SpellCheckerSettingsFragment extends PreferenceFragment { addPreferencesFromResource(R.xml.spell_checker_settings); final PreferenceScreen preferenceScreen = getPreferenceScreen(); if (preferenceScreen != null) { - preferenceScreen.setTitle(Utils.getAcitivityTitleResId( + preferenceScreen.setTitle(ApplicationUtils.getAcitivityTitleResId( getActivity(), SpellCheckerSettingsActivity.class)); } } diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 2218b3bc6..e97069dff 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -24,14 +24,13 @@ import android.graphics.drawable.Drawable; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; -import com.android.inputmethod.keyboard.TypefaceUtils; 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.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreSuggestions extends Keyboard { public static final int SUGGESTION_CODE_BASE = 1024; @@ -207,7 +206,7 @@ public final class MoreSuggestions extends Keyboard { final int y = params.getY(index); final int width = params.getWidth(index); final String word = mSuggestedWords.getWord(index); - final String info = Utils.getDebugInfo(mSuggestedWords, index); + final String info = mSuggestedWords.getDebugString(index); final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE; final Key key = new Key( params, word, info, KeyboardIconsSet.ICON_UNDEFINED, indexInMoreSuggestions, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 9565f63f7..ce340b666 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -45,13 +45,12 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; -import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.ResourceUtils; -import com.android.inputmethod.latin.utils.Utils; +import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayList; @@ -446,7 +445,7 @@ final class SuggestionStripLayoutHelper { wordView.setTextColor(getSuggestionTextColor(positionInStrip, suggestedWords)); if (SuggestionStripView.DBG) { mDebugInfoViews.get(positionInStrip).setText( - Utils.getDebugInfo(suggestedWords, indexInSuggestedWords)); + suggestedWords.getDebugString(indexInSuggestedWords)); } } } diff --git a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java new file mode 100644 index 000000000..08a2a8c5a --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java @@ -0,0 +1,65 @@ +/* + * 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 getAcitivityTitleResId(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 ""; + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index b3d37d78c..34eccd65b 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -20,7 +20,6 @@ import android.content.ContentValues; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; -import android.text.format.DateUtils; import android.util.Log; import com.android.inputmethod.latin.AssetFileAddress; @@ -35,6 +34,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; +import java.util.concurrent.TimeUnit; /** * This class encapsulates the logic for the Latin-IME side of dictionary information management. @@ -74,8 +74,8 @@ public class DictionaryInfoUtils { values.put(LOCALE_COLUMN, mLocale.toString()); values.put(DESCRIPTION_COLUMN, mDescription); values.put(LOCAL_FILENAME_COLUMN, mFileAddress.mFilename); - values.put(DATE_COLUMN, - new File(mFileAddress.mFilename).lastModified() / DateUtils.SECOND_IN_MILLIS); + values.put(DATE_COLUMN, TimeUnit.MILLISECONDS.toSeconds( + new File(mFileAddress.mFilename).lastModified())); values.put(FILESIZE_COLUMN, mFileAddress.mLength); values.put(VERSION_COLUMN, mVersion); return values; diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java new file mode 100644 index 000000000..e958a7e71 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java @@ -0,0 +1,77 @@ +/* + * 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 com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.WordComposer; + +public final class LatinImeLoggerUtils { + private LatinImeLoggerUtils() { + // This utility class is not publicly instantiable. + } + + public static void onNonSeparator(final char code, final int x, final int y) { + UserLogRingCharBuffer.getInstance().push(code, x, y); + LatinImeLogger.logOnInputChar(); + } + + public static void onSeparator(final int code, final int x, final int y) { + // Helper method to log a single code point separator + // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils + onSeparator(new String(new int[]{code}, 0, 1), x, y); + } + + public static void onSeparator(final String separator, final int x, final int y) { + final int length = separator.length(); + for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) { + int codePoint = Character.codePointAt(separator, i); + // TODO: accept code points + UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y); + } + LatinImeLogger.logOnInputSeparator(); + } + + public static void onAutoCorrection(final String typedWord, final String correctedWord, + final String separatorString, final WordComposer wordComposer) { + final boolean isBatchMode = wordComposer.isBatchMode(); + if (!isBatchMode && TextUtils.isEmpty(typedWord)) { + return; + } + // TODO: this fails when the separator is more than 1 code point long, but + // the backend can't handle it yet. The only case when this happens is with + // smileys and other multi-character keys. + final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE + : separatorString.codePointAt(0); + if (!isBatchMode) { + LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint); + } else { + if (!TextUtils.isEmpty(correctedWord)) { + // We must make sure that InputPointer contains only the relative timestamps, + // not actual timestamps. + LatinImeLogger.logOnAutoCorrectionForGeometric( + "", correctedWord, codePoint, wordComposer.getInputPointers()); + } + } + } + + public static void onAutoCorrectionCancellation() { + LatinImeLogger.logOnAutoCorrectionCancelled(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java new file mode 100644 index 000000000..5793e4170 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/TextRange.java @@ -0,0 +1,116 @@ +/* + * 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 int getNumberOfCharsInWordBeforeCursor() { + return mCursorIndex - mWordAtCursorStartIndex; + } + + public int getNumberOfCharsInWordAfterCursor() { + return mWordAtCursorEndIndex - mCursorIndex; + } + + /** + * 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, we 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) { + if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex + || cursorIndex > wordAtCursorEndIndex + || wordAtCursorEndIndex > textAtCursor.length()) { + throw new IndexOutOfBoundsException(); + } + mTextAtCursor = textAtCursor; + mWordAtCursorStartIndex = wordAtCursorStartIndex; + mWordAtCursorEndIndex = wordAtCursorEndIndex; + mCursorIndex = cursorIndex; + mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex); + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/keyboard/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java index c3b952037..544e4d201 100644 --- a/java/src/com/android/inputmethod/keyboard/TypefaceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard; +package com.android.inputmethod.latin.utils; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.util.SparseArray; -import com.android.inputmethod.latin.utils.CollectionUtils; - public final class TypefaceUtils { private TypefaceUtils() { // This utility class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java new file mode 100644 index 000000000..ef9cacf61 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java @@ -0,0 +1,255 @@ +/* + * 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.latin.utils; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.inputmethodservice.InputMethodService; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.util.Log; + +import com.android.inputmethod.latin.LatinImeLogger; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.channels.FileChannel; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public final class UsabilityStudyLogUtils { + // TODO: remove code duplication with ResearchLog class + private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); + private static final String FILENAME = "log.txt"; + private final Handler mLoggingHandler; + private File mFile; + private File mDirectory; + private InputMethodService mIms; + private PrintWriter mWriter; + private final Date mDate; + private final SimpleDateFormat mDateFormat; + + private UsabilityStudyLogUtils() { + mDate = new Date(); + mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US); + + HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task", + Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + mLoggingHandler = new Handler(handlerThread.getLooper()); + } + + // Initialization-on-demand holder + private static final class OnDemandInitializationHolder { + public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); + } + + public static UsabilityStudyLogUtils getInstance() { + return OnDemandInitializationHolder.sInstance; + } + + public void init(final InputMethodService ims) { + mIms = ims; + mDirectory = ims.getFilesDir(); + } + + private void createLogFileIfNotExist() { + if ((mFile == null || !mFile.exists()) + && (mDirectory != null && mDirectory.exists())) { + try { + mWriter = getPrintWriter(mDirectory, FILENAME, false); + } catch (final IOException e) { + Log.e(USABILITY_TAG, "Can't create log file."); + } + } + } + + public static void writeBackSpace(final int x, final int y) { + UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); + } + + public static void writeChar(final char c, final int x, final int y) { + String inputChar = String.valueOf(c); + switch (c) { + case '\n': + inputChar = "<enter>"; + break; + case '\t': + inputChar = "<tab>"; + break; + case ' ': + inputChar = "<space>"; + break; + } + UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y); + LatinImeLogger.onPrintAllUsabilityStudyLogs(); + } + + public void write(final String log) { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + createLogFileIfNotExist(); + final long currentTime = System.currentTimeMillis(); + mDate.setTime(currentTime); + + final String printString = String.format(Locale.US, "%s\t%d\t%s\n", + mDateFormat.format(mDate), currentTime, log); + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Write: " + log); + } + mWriter.print(printString); + } + }); + } + + private synchronized String getBufferedLogs() { + mWriter.flush(); + final StringBuilder sb = new StringBuilder(); + final BufferedReader br = getBufferedReader(); + String line; + try { + while ((line = br.readLine()) != null) { + sb.append('\n'); + sb.append(line); + } + } catch (final IOException e) { + Log.e(USABILITY_TAG, "Can't read log file."); + } finally { + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString()); + } + try { + br.close(); + } catch (final IOException e) { + // ignore. + } + } + return sb.toString(); + } + + public void emailResearcherLogsAll() { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + final Date date = new Date(); + date.setTime(System.currentTimeMillis()); + final String currentDateTimeString = + new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date); + if (mFile == null) { + Log.w(USABILITY_TAG, "No internal log file found."); + return; + } + if (mIms.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); + return; + } + mWriter.flush(); + final String destPath = Environment.getExternalStorageDirectory() + + "/research-" + currentDateTimeString + ".log"; + final File destFile = new File(destPath); + try { + final FileInputStream srcStream = new FileInputStream(mFile); + final FileOutputStream destStream = new FileOutputStream(destFile); + final FileChannel src = srcStream.getChannel(); + final FileChannel dest = destStream.getChannel(); + src.transferTo(0, src.size(), dest); + src.close(); + srcStream.close(); + dest.close(); + destStream.close(); + } catch (final FileNotFoundException e1) { + Log.w(USABILITY_TAG, e1); + return; + } catch (final IOException e2) { + Log.w(USABILITY_TAG, e2); + return; + } + if (destFile == null || !destFile.exists()) { + Log.w(USABILITY_TAG, "Dest file doesn't exist."); + return; + } + final Intent intent = new Intent(Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); + } + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); + intent.putExtra(Intent.EXTRA_SUBJECT, + "[Research Logs] " + currentDateTimeString); + mIms.startActivity(intent); + } + }); + } + + public void printAll() { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0); + } + }); + } + + public void clearAll() { + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + if (mFile != null && mFile.exists()) { + if (LatinImeLogger.sDBG) { + Log.d(USABILITY_TAG, "Delete log file."); + } + mFile.delete(); + mWriter.close(); + } + } + }); + } + + private BufferedReader getBufferedReader() { + createLogFileIfNotExist(); + try { + return new BufferedReader(new FileReader(mFile)); + } catch (final FileNotFoundException e) { + return null; + } + } + + private PrintWriter getPrintWriter(final File dir, final String filename, + final boolean renew) throws IOException { + mFile = new File(dir, filename); + if (mFile.exists()) { + if (renew) { + mFile.delete(); + } + } + return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java index 9f842f976..713a45bda 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java @@ -16,9 +16,10 @@ package com.android.inputmethod.latin.utils; -import android.text.format.DateUtils; import android.util.Log; +import java.util.concurrent.TimeUnit; + public final class UserHistoryForgettingCurveUtils { private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName(); private static final boolean DEBUG = false; @@ -27,8 +28,8 @@ public final class UserHistoryForgettingCurveUtils { private static final int FC_LEVEL_MAX = 3; /* package */ static final int ELAPSED_TIME_MAX = 15; private static final int ELAPSED_TIME_INTERVAL_HOURS = 6; - private static final long ELAPSED_TIME_INTERVAL_MILLIS = ELAPSED_TIME_INTERVAL_HOURS - * DateUtils.HOUR_IN_MILLIS; + private static final long ELAPSED_TIME_INTERVAL_MILLIS = + TimeUnit.HOURS.toMillis(ELAPSED_TIME_INTERVAL_HOURS); private static final int HALF_LIFE_HOURS = 48; private static final int MAX_PUSH_ELAPSED = (FC_LEVEL_MAX + 1) * (ELAPSED_TIME_MAX + 1); diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java index 3e67e8216..a2c6c4524 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java +++ b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java @@ -20,7 +20,6 @@ import android.inputmethodservice.InputMethodService; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Settings; -import com.android.inputmethod.latin.utils.Utils.UsabilityStudyLogUtils; public final class UserLogRingCharBuffer { public /* for test */ static final int BUFSIZE = 20; diff --git a/java/src/com/android/inputmethod/latin/utils/Utils.java b/java/src/com/android/inputmethod/latin/utils/Utils.java deleted file mode 100644 index c4e18ed7e..000000000 --- a/java/src/com/android/inputmethod/latin/utils/Utils.java +++ /dev/null @@ -1,374 +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.utils; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.inputmethodservice.InputMethodService; -import android.net.Uri; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.text.TextUtils; -import android.util.Log; - -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.WordComposer; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.channels.FileChannel; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -// TODO: Come up with a more descriptive class name -public final class Utils { - private static final String TAG = Utils.class.getSimpleName(); - - private Utils() { - // This utility class is not publicly instantiable. - } - - // TODO: Make this an external class - public static final class UsabilityStudyLogUtils { - // TODO: remove code duplication with ResearchLog class - private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); - private static final String FILENAME = "log.txt"; - private final Handler mLoggingHandler; - private File mFile; - private File mDirectory; - private InputMethodService mIms; - private PrintWriter mWriter; - private final Date mDate; - private final SimpleDateFormat mDateFormat; - - private UsabilityStudyLogUtils() { - mDate = new Date(); - mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US); - - HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task", - Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - mLoggingHandler = new Handler(handlerThread.getLooper()); - } - - // Initialization-on-demand holder - private static final class OnDemandInitializationHolder { - public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); - } - - public static UsabilityStudyLogUtils getInstance() { - return OnDemandInitializationHolder.sInstance; - } - - public void init(final InputMethodService ims) { - mIms = ims; - mDirectory = ims.getFilesDir(); - } - - private void createLogFileIfNotExist() { - if ((mFile == null || !mFile.exists()) - && (mDirectory != null && mDirectory.exists())) { - try { - mWriter = getPrintWriter(mDirectory, FILENAME, false); - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't create log file."); - } - } - } - - public static void writeBackSpace(final int x, final int y) { - UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); - } - - public static void writeChar(final char c, final int x, final int y) { - String inputChar = String.valueOf(c); - switch (c) { - case '\n': - inputChar = "<enter>"; - break; - case '\t': - inputChar = "<tab>"; - break; - case ' ': - inputChar = "<space>"; - break; - } - UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y); - LatinImeLogger.onPrintAllUsabilityStudyLogs(); - } - - public void write(final String log) { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - createLogFileIfNotExist(); - final long currentTime = System.currentTimeMillis(); - mDate.setTime(currentTime); - - final String printString = String.format(Locale.US, "%s\t%d\t%s\n", - mDateFormat.format(mDate), currentTime, log); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Write: " + log); - } - mWriter.print(printString); - } - }); - } - - private synchronized String getBufferedLogs() { - mWriter.flush(); - final StringBuilder sb = new StringBuilder(); - final BufferedReader br = getBufferedReader(); - String line; - try { - while ((line = br.readLine()) != null) { - sb.append('\n'); - sb.append(line); - } - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't read log file."); - } finally { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString()); - } - try { - br.close(); - } catch (final IOException e) { - // ignore. - } - } - return sb.toString(); - } - - public void emailResearcherLogsAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - final Date date = new Date(); - date.setTime(System.currentTimeMillis()); - final String currentDateTimeString = - new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date); - if (mFile == null) { - Log.w(USABILITY_TAG, "No internal log file found."); - return; - } - if (mIms.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); - return; - } - mWriter.flush(); - final String destPath = Environment.getExternalStorageDirectory() - + "/research-" + currentDateTimeString + ".log"; - final File destFile = new File(destPath); - try { - final FileInputStream srcStream = new FileInputStream(mFile); - final FileOutputStream destStream = new FileOutputStream(destFile); - final FileChannel src = srcStream.getChannel(); - final FileChannel dest = destStream.getChannel(); - src.transferTo(0, src.size(), dest); - src.close(); - srcStream.close(); - dest.close(); - destStream.close(); - } catch (final FileNotFoundException e1) { - Log.w(USABILITY_TAG, e1); - return; - } catch (final IOException e2) { - Log.w(USABILITY_TAG, e2); - return; - } - if (destFile == null || !destFile.exists()) { - Log.w(USABILITY_TAG, "Dest file doesn't exist."); - return; - } - final Intent intent = new Intent(Intent.ACTION_SEND); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); - } - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); - intent.putExtra(Intent.EXTRA_SUBJECT, - "[Research Logs] " + currentDateTimeString); - mIms.startActivity(intent); - } - }); - } - - public void printAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0); - } - }); - } - - public void clearAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - if (mFile != null && mFile.exists()) { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Delete log file."); - } - mFile.delete(); - mWriter.close(); - } - } - }); - } - - private BufferedReader getBufferedReader() { - createLogFileIfNotExist(); - try { - return new BufferedReader(new FileReader(mFile)); - } catch (final FileNotFoundException e) { - return null; - } - } - - private PrintWriter getPrintWriter(final File dir, final String filename, - final boolean renew) throws IOException { - mFile = new File(dir, filename); - if (mFile.exists()) { - if (renew) { - mFile.delete(); - } - } - return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); - } - } - - // TODO: Make this an external class - public static final class Stats { - public static void onNonSeparator(final char code, final int x, final int y) { - UserLogRingCharBuffer.getInstance().push(code, x, y); - LatinImeLogger.logOnInputChar(); - } - - public static void onSeparator(final int code, final int x, final int y) { - // Helper method to log a single code point separator - // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils - onSeparator(new String(new int[]{code}, 0, 1), x, y); - } - - public static void onSeparator(final String separator, final int x, final int y) { - final int length = separator.length(); - for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) { - int codePoint = Character.codePointAt(separator, i); - // TODO: accept code points - UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y); - } - LatinImeLogger.logOnInputSeparator(); - } - - public static void onAutoCorrection(final String typedWord, final String correctedWord, - final String separatorString, final WordComposer wordComposer) { - final boolean isBatchMode = wordComposer.isBatchMode(); - if (!isBatchMode && TextUtils.isEmpty(typedWord)) { - return; - } - // TODO: this fails when the separator is more than 1 code point long, but - // the backend can't handle it yet. The only case when this happens is with - // smileys and other multi-character keys. - final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE - : separatorString.codePointAt(0); - if (!isBatchMode) { - LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint); - } else { - if (!TextUtils.isEmpty(correctedWord)) { - // We must make sure that InputPointer contains only the relative timestamps, - // not actual timestamps. - LatinImeLogger.logOnAutoCorrectionForGeometric( - "", correctedWord, codePoint, wordComposer.getInputPointers()); - } - } - } - - public static void onAutoCorrectionCancellation() { - LatinImeLogger.logOnAutoCorrectionCancelled(); - } - } - - public static String getDebugInfo(final SuggestedWords suggestions, final int pos) { - if (!LatinImeLogger.sDBG) { - return null; - } - final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); - if (wordInfo == null) { - return null; - } - final String info = wordInfo.getDebugString(); - if (TextUtils.isEmpty(info)) { - return null; - } - return info; - } - - public static int getAcitivityTitleResId(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 ""; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java index dc12fa468..f9d853493 100644 --- a/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard; +package com.android.inputmethod.latin.utils; import android.view.View; import android.view.ViewGroup; @@ -27,7 +27,8 @@ public final class ViewLayoutUtils { // This utility class is not publicly instantiable. } - public static MarginLayoutParams newLayoutParam(ViewGroup placer, int width, int height) { + 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) { @@ -40,7 +41,8 @@ public final class ViewLayoutUtils { } } - public static void placeViewAt(View view, int x, int y, int w, int h) { + 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; diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 88207c024..46e620ae5 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -55,7 +55,7 @@ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; + private static final long FLUSH_DELAY_IN_MS = TimeUnit.SECONDS.toMillis(5); /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index ecf809242..ed047e13a 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,7 +37,6 @@ import android.os.IBinder; import android.os.SystemClock; import android.preference.PreferenceManager; import android.text.TextUtils; -import android.text.format.DateUtils; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -57,11 +56,11 @@ import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputConnection; -import com.android.inputmethod.latin.RichInputConnection.Range; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.InputTypeUtils; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.MotionEventReader.ReplayData; import com.android.inputmethod.research.ui.SplashScreen; @@ -75,6 +74,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; // TODO: Add a unit test for every "logging" method (i.e. that is called from the IME and calls @@ -137,10 +137,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_SAVED_CHANNEL = "pref_research_saved_channel"; - private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = 5 * 1000; - private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = 5 * 1000; - private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS; - private static final long MAX_LOGFILE_AGE_IN_MS = 4 * DateUtils.DAY_IN_MILLIS; + private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = TimeUnit.SECONDS.toMillis(5); + private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = TimeUnit.SECONDS.toMillis(5); + private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = TimeUnit.DAYS.toMillis(1); + private static final long MAX_LOGFILE_AGE_IN_MS = TimeUnit.DAYS.toMillis(4); private static final ResearchLogger sInstance = new ResearchLogger(); private static String sAccountType = null; @@ -195,7 +195,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // not performed on text that the user types into the feedback dialog. private boolean mInFeedbackDialog = false; private Handler mUserRecordingTimeoutHandler; - private static final long USER_RECORDING_TIMEOUT_MS = 30L * DateUtils.SECOND_IN_MILLIS; + private static final long USER_RECORDING_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(30); // Stores a temporary LogUnit while generating a phantom space. Needed because phantom spaces // are issued out-of-order, immediately before the characters generated by other operations that @@ -542,8 +542,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang toast.show(); boolean isLogDeleted = abort(); final long currentTime = System.currentTimeMillis(); - final long resumeTime = currentTime + 1000 * 60 * - SUSPEND_DURATION_IN_MINUTES; + final long resumeTime = currentTime + + TimeUnit.MINUTES.toMillis(SUSPEND_DURATION_IN_MINUTES); suspendLoggingUntil(resumeTime); toast.cancel(); Toast.makeText(latinIME, R.string.research_notify_logging_suspended, @@ -635,7 +635,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMotionEventReader.readMotionEventData(mUserRecordingFile); mReplayer.replay(replayData, null); } - }, 1000); + }, TimeUnit.SECONDS.toMillis(1)); } if (FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD) { @@ -1220,7 +1220,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final RichInputConnection connection) { String word = ""; if (connection != null) { - Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); + TextRange range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); if (range != null) { word = range.mWord.toString(); } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index e573ca012..fd323a104 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -21,6 +21,8 @@ import android.util.Log; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.define.ProductionFlag; +import java.util.concurrent.TimeUnit; + public class Statistics { private static final String TAG = Statistics.class.getSimpleName(); private static final boolean DEBUG = false @@ -102,8 +104,8 @@ public class Statistics { // To account for the interruptions when the user's attention is directed elsewhere, times // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds - public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds + public static final long MIN_TYPING_INTERMISSION = TimeUnit.SECONDS.toMillis(2); + public static final long MIN_DELETION_INTERMISSION = TimeUnit.SECONDS.toMillis(10); // The last time that a tap was performed private long mLastTapTime; diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 8bd46c19e..fd3f2f60e 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -34,7 +34,6 @@ public final class UploaderService extends IntentService { public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; public static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() + ".extra.UPLOAD_UNCONDITIONALLY"; - protected static final int TIMEOUT_IN_MS = 1000 * 4; public UploaderService() { super("Research Uploader Service"); |