diff options
Diffstat (limited to 'java/src')
30 files changed, 559 insertions, 314 deletions
diff --git a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java new file mode 100644 index 000000000..eb180071e --- /dev/null +++ b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.app.Notification; +import android.os.Build; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class NotificationCompatUtils { + // Note that TextInfo.getCharSequence() is supposed to be available in API level 21 and later. + private static final Method METHOD_setColor = + CompatUtils.getMethod(Notification.Builder.class, "setColor", int.class); + private static final Method METHOD_setVisibility = + CompatUtils.getMethod(Notification.Builder.class, "setVisibility", int.class); + private static final Method METHOD_setCategory = + CompatUtils.getMethod(Notification.Builder.class, "setCategory", String.class); + private static final Method METHOD_setPriority = + CompatUtils.getMethod(Notification.Builder.class, "setPriority", int.class); + private static final Method METHOD_build = + CompatUtils.getMethod(Notification.Builder.class, "build"); + private static final Field FIELD_VISIBILITY_SECRET = + CompatUtils.getField(Notification.class, "VISIBILITY_SECRET"); + private static final int VISIBILITY_SECRET = null == FIELD_VISIBILITY_SECRET ? 0 + : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */, + FIELD_VISIBILITY_SECRET); + private static final Field FIELD_CATEGORY_RECOMMENDATION = + CompatUtils.getField(Notification.class, "CATEGORY_RECOMMENDATION"); + private static final String CATEGORY_RECOMMENDATION = null == FIELD_CATEGORY_RECOMMENDATION ? "" + : (String) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */, + FIELD_CATEGORY_RECOMMENDATION); + private static final Field FIELD_PRIORITY_LOW = + CompatUtils.getField(Notification.class, "PRIORITY_LOW"); + private static final int PRIORITY_LOW = null == FIELD_PRIORITY_LOW ? 0 + : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */, + FIELD_PRIORITY_LOW); + + private NotificationCompatUtils() { + // This class is non-instantiable. + } + + // Sets the accent color + public static void setColor(final Notification.Builder builder, final int color) { + CompatUtils.invoke(builder, null, METHOD_setColor, color); + } + + public static void setVisibilityToSecret(final Notification.Builder builder) { + CompatUtils.invoke(builder, null, METHOD_setVisibility, VISIBILITY_SECRET); + } + + public static void setCategoryToRecommendation(final Notification.Builder builder) { + CompatUtils.invoke(builder, null, METHOD_setCategory, CATEGORY_RECOMMENDATION); + } + + public static void setPriorityToLow(final Notification.Builder builder) { + CompatUtils.invoke(builder, null, METHOD_setPriority, PRIORITY_LOW); + } + + public static Notification build(final Notification.Builder builder) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + // #build was added in API level 16, JELLY_BEAN + return (Notification) CompatUtils.invoke(builder, null, METHOD_build); + } else { + // #getNotification was deprecated in API level 16, JELLY_BEAN + return builder.getNotification(); + } + } +} diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java index afbe8c890..0f00be133 100644 --- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java @@ -34,6 +34,9 @@ public final class ViewCompatUtils { // Note that View.setElevation(float) has been introduced in API level 21. private static final Method METHOD_setElevation = CompatUtils.getMethod( View.class, "setElevation", float.class); + // Note that View.setTextAlignment(int) has been introduced in API level 17. + private static final Method METHOD_setTextAlignment = CompatUtils.getMethod( + View.class, "setTextAlignment", int.class); private ViewCompatUtils() { // This utility class is not publicly instantiable. @@ -56,9 +59,19 @@ public final class ViewCompatUtils { } public static void setElevation(final View view, final float elevation) { - if (METHOD_setElevation == null) { - return; - } CompatUtils.invoke(view, null, METHOD_setElevation, elevation); } + + // These TEXT_ALIGNMENT_* constants have been introduced in API 17. + public static final int TEXT_ALIGNMENT_INHERIT = 0; + public static final int TEXT_ALIGNMENT_GRAVITY = 1; + public static final int TEXT_ALIGNMENT_TEXT_START = 2; + public static final int TEXT_ALIGNMENT_TEXT_END = 3; + public static final int TEXT_ALIGNMENT_CENTER = 4; + public static final int TEXT_ALIGNMENT_VIEW_START = 5; + public static final int TEXT_ALIGNMENT_VIEW_END = 6; + + public static void setTextAlignment(final View view, final int textAlignment) { + CompatUtils.invoke(view, null, METHOD_setTextAlignment, textAlignment); + } } diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index 95a094232..6fbca44c5 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -31,12 +31,14 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.ConnectivityManager; import android.net.Uri; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.compat.ConnectivityManagerCompatUtils; import com.android.inputmethod.compat.DownloadManagerCompatUtils; +import com.android.inputmethod.compat.NotificationCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; @@ -858,7 +860,7 @@ public final class UpdateHandler { final String language = (null == locale ? "" : locale.getDisplayLanguage()); final String titleFormat = context.getString(R.string.dict_available_notification_title); final String notificationTitle = String.format(titleFormat, language); - final Notification notification = new Notification.Builder(context) + final Notification.Builder builder = new Notification.Builder(context) .setAutoCancel(true) .setContentIntent(notificationIntent) .setContentTitle(notificationTitle) @@ -866,8 +868,13 @@ public final class UpdateHandler { .setTicker(notificationTitle) .setOngoing(false) .setOnlyAlertOnce(true) - .setSmallIcon(R.drawable.ic_notify_dictionary) - .getNotification(); + .setSmallIcon(R.drawable.ic_notify_dictionary); + NotificationCompatUtils.setColor(builder, + context.getResources().getColor(R.color.notification_accent_color)); + NotificationCompatUtils.setPriorityToLow(builder); + NotificationCompatUtils.setVisibilityToSecret(builder); + NotificationCompatUtils.setCategoryToRecommendation(builder); + final Notification notification = NotificationCompatUtils.build(builder); notificationManager.notify(DICT_AVAILABLE_NOTIFICATION_ID, notification); } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 3743d26e6..05334c7f4 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -108,11 +108,23 @@ public class Key implements Comparable<Key> { private final MoreKeySpec[] mMoreKeys; /** More keys column number and flags */ private final int mMoreKeysColumnAndFlags; - private static final int MORE_KEYS_COLUMN_MASK = 0x000000ff; - private static final int MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER = 0x80000000; + private static final int MORE_KEYS_COLUMN_NUMBER_MASK = 0x000000ff; + // If this flag is specified, more keys keyboard should have the specified number of columns. + // Otherwise more keys keyboard should have less than or equal to the specified maximum number + // of columns. + private static final int MORE_KEYS_FLAGS_FIXED_COLUMN = 0x00000100; + // If this flag is specified, the order of more keys is determined by the order in the more + // keys' specification. Otherwise the order of more keys is automatically determined. + private static final int MORE_KEYS_FLAGS_FIXED_ORDER = 0x00000200; + private static final int MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER = 0; + private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER = + MORE_KEYS_FLAGS_FIXED_COLUMN; + private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER = + (MORE_KEYS_FLAGS_FIXED_COLUMN | MORE_KEYS_FLAGS_FIXED_ORDER); private static final int MORE_KEYS_FLAGS_HAS_LABELS = 0x40000000; private static final int MORE_KEYS_FLAGS_NEEDS_DIVIDERS = 0x20000000; private static final int MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY = 0x10000000; + // TODO: Rename these specifiers to !autoOrder! and !fixedOrder! respectively. private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!"; private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!"; private static final String MORE_KEYS_HAS_LABELS = "!hasLabels!"; @@ -255,25 +267,31 @@ public class Key implements Comparable<Key> { int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); - int moreKeysColumn = style.getInt(keyAttr, - R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn); + // Get maximum column order number and set a relevant mode value. + int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER + | style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn, + params.mMaxMoreKeysKeyboardColumn); int value; if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) { - moreKeysColumn = value & MORE_KEYS_COLUMN_MASK; + // Override with fixed column order number and set a relevant mode value. + moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER + | (value & MORE_KEYS_COLUMN_NUMBER_MASK); } if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) { - moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK); + // Override with fixed column order number and set a relevant mode value. + moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER + | (value & MORE_KEYS_COLUMN_NUMBER_MASK); } if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) { - moreKeysColumn |= MORE_KEYS_FLAGS_HAS_LABELS; + moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS; } if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) { - moreKeysColumn |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS; + moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS; } if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) { - moreKeysColumn |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY; + moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY; } - mMoreKeysColumnAndFlags = moreKeysColumn; + mMoreKeysColumnAndFlags = moreKeysColumnAndFlags; final String[] additionalMoreKeys; if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) { @@ -680,12 +698,16 @@ public class Key implements Comparable<Key> { && !TextUtils.isEmpty(mHintLabel); } - public final int getMoreKeysColumn() { - return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK; + public final int getMoreKeysColumnNumber() { + return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_NUMBER_MASK; } - public final boolean isFixedColumnOrderMoreKeys() { - return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0; + public final boolean isMoreKeysFixedColumn() { + return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN) != 0; + } + + public final boolean isMoreKeysFixedOrder() { + return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_ORDER) != 0; } public final boolean hasLabelsInMoreKeys() { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 0804cebc4..3f4367313 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -119,7 +119,15 @@ public final class KeyboardLayoutSet { new SparseArray<>(); } - public static void clearKeyboardCache() { + public static void onSystemLocaleChanged() { + clearKeyboardCache(); + } + + public static void onKeyboardThemeChanged() { + clearKeyboardCache(); + } + + private static void clearKeyboardCache() { sKeyboardCache.clear(); sKeysCache.clear(); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 30bd1dfda..91d703330 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -103,7 +103,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) { mKeyboardTheme = keyboardTheme; mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); - KeyboardLayoutSet.clearKeyboardCache(); + KeyboardLayoutSet.onKeyboardThemeChanged(); return true; } return false; @@ -154,9 +154,12 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mCurrentSettingsValues.mKeyPreviewPopupOn, mCurrentSettingsValues.mKeyPreviewPopupDismissDelay); keyboardView.setKeyPreviewAnimationParams( - mCurrentSettingsValues.mKeyPreviewShowUpStartScale, + mCurrentSettingsValues.mHasCustomKeyPreviewAnimationParams, + mCurrentSettingsValues.mKeyPreviewShowUpStartXScale, + mCurrentSettingsValues.mKeyPreviewShowUpStartYScale, mCurrentSettingsValues.mKeyPreviewShowUpDuration, - mCurrentSettingsValues.mKeyPreviewDismissEndScale, + mCurrentSettingsValues.mKeyPreviewDismissEndXScale, + mCurrentSettingsValues.mKeyPreviewDismissEndYScale, mCurrentSettingsValues.mKeyPreviewDismissDuration); keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); final boolean subtypeChanged = (oldKeyboard == null) diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 847d90711..d2f3e9714 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -85,6 +85,8 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout + * @attr ref R.styleable#MainKeyboardView_keyPreviewShowUpAnimator + * @attr ref R.styleable#MainKeyboardView_keyPreviewDismissAnimator * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint @@ -390,20 +392,34 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } /** - * Enables or disables the key feedback popup. This is a popup that shows a magnified + * Enables or disables the key preview popup. This is a popup that shows a magnified * version of the depressed key. By default the preview is enabled. * @param previewEnabled whether or not to enable the key feedback preview * @param delay the delay after which the preview is dismissed - * @see #isKeyPreviewPopupEnabled() */ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) { mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay); } - public void setKeyPreviewAnimationParams(final float showUpStartScale, final int showUpDuration, - final float dismissEndScale, final int dismissDuration) { - mKeyPreviewDrawParams.setAnimationParams( - showUpStartScale, showUpDuration, dismissEndScale, dismissDuration); + /** + * Enables or disables the key preview popup animations and set animations' parameters. + * + * @param hasCustomAnimationParams false to use the default key preview popup animations + * specified by keyPreviewShowUpAnimator and keyPreviewDismissAnimator attributes. + * true to override the default animations with the specified parameters. + * @param showUpStartXScale from this x-scale the show up animation will start. + * @param showUpStartYScale from this y-scale the show up animation will start. + * @param showUpDuration the duration of the show up animation in milliseconds. + * @param dismissEndXScale to this x-scale the dismiss animation will end. + * @param dismissEndYScale to this y-scale the dismiss animation will end. + * @param dismissDuration the duration of the dismiss animation in milliseconds. + */ + public void setKeyPreviewAnimationParams(final boolean hasCustomAnimationParams, + final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration, + final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) { + mKeyPreviewDrawParams.setAnimationParams(hasCustomAnimationParams, + showUpStartXScale, showUpStartYScale, showUpDuration, + dismissEndXScale, dismissEndYScale, dismissDuration); } private void locatePreviewPlacerView() { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index e0184d7f1..52e2e85ba 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -43,7 +43,7 @@ public final class MoreKeysKeyboard extends Keyboard { @UsedForTesting static class MoreKeysKeyboardParams extends KeyboardParams { - public boolean mIsFixedOrder; + public boolean mIsMoreKeysFixedOrder; /* package */int mTopRowAdjustment; public int mNumRows; public int mNumColumns; @@ -61,29 +61,35 @@ public final class MoreKeysKeyboard extends Keyboard { * Set keyboard parameters of more keys keyboard. * * @param numKeys number of keys in this more keys keyboard. - * @param maxColumns number of maximum columns of this more keys keyboard. + * @param numColumn number of columns of this more keys keyboard. * @param keyWidth more keys keyboard key width in pixel, including horizontal gap. * @param rowHeight more keys keyboard row height in pixel, including vertical gap. * @param coordXInParent coordinate x of the key preview in parent keyboard. * @param parentKeyboardWidth parent keyboard width in pixel. - * @param isFixedColumnOrder if true, more keys should be laid out in fixed order. + * @param isMoreKeysFixedColumn true if more keys keyboard should have + * <code>numColumn</code> columns. Otherwise more keys keyboard should have + * <code>numColumn</code> columns at most. + * @param isMoreKeysFixedOrder true if the order of more keys is determined by the order in + * the more keys' specification. Otherwise the order of more keys is automatically + * determined. * @param dividerWidth width of divider, zero for no dividers. */ - public void setParameters(final int numKeys, final int maxColumns, final int keyWidth, + public void setParameters(final int numKeys, final int numColumn, final int keyWidth, final int rowHeight, final int coordXInParent, final int parentKeyboardWidth, - final boolean isFixedColumnOrder, final int dividerWidth) { - mIsFixedOrder = isFixedColumnOrder; - if (parentKeyboardWidth / keyWidth < Math.min(numKeys, maxColumns)) { + final boolean isMoreKeysFixedColumn, final boolean isMoreKeysFixedOrder, + final int dividerWidth) { + mIsMoreKeysFixedOrder = isMoreKeysFixedOrder; + if (parentKeyboardWidth / keyWidth < Math.min(numKeys, numColumn)) { throw new IllegalArgumentException("Keyboard is too small to hold more keys: " - + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + maxColumns); + + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + numColumn); } mDefaultKeyWidth = keyWidth; mDefaultRowHeight = rowHeight; - final int numRows = (numKeys + maxColumns - 1) / maxColumns; + final int numRows = (numKeys + numColumn - 1) / numColumn; mNumRows = numRows; - final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns) - : getOptimizedColumns(numKeys, maxColumns); + final int numColumns = isMoreKeysFixedColumn ? Math.min(numKeys, numColumn) + : getOptimizedColumns(numKeys, numColumn); mNumColumns = numColumns; final int topKeys = numKeys % numColumns; mTopKeys = topKeys == 0 ? numColumns : topKeys; @@ -120,7 +126,7 @@ public final class MoreKeysKeyboard extends Keyboard { mRightKeys = rightKeys; // Adjustment of the top row. - mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment() + mTopRowAdjustment = isMoreKeysFixedOrder ? getFixedOrderTopRowAdjustment() : getAutoOrderTopRowAdjustment(); mDividerWidth = dividerWidth; mColumnWidth = mDefaultKeyWidth + mDividerWidth; @@ -148,7 +154,7 @@ public final class MoreKeysKeyboard extends Keyboard { // Return key position according to column count (0 is default). /* package */int getColumnPos(final int n) { - return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n); + return mIsMoreKeysFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n); } private int getFixedOrderColumnPos(final int n) { @@ -263,7 +269,8 @@ public final class MoreKeysKeyboard extends Keyboard { * @param keyboard the {@link Keyboard} that contains the parentKey. * @param isSingleMoreKeyWithPreview true if the <code>key</code> has just a single * "more key" and its key popup preview is enabled. - * @param keyPreviewDrawParams the parameter to place key preview. + * @param keyPreviewVisibleWidth the width of visible part of key popup preview. + * @param keyPreviewVisibleHeight the height of visible part of key popup preview * @param paintToMeasure the {@link Paint} object to measure a "more key" width */ public Builder(final Context context, final Key key, final Keyboard keyboard, @@ -306,9 +313,9 @@ public final class MoreKeysKeyboard extends Keyboard { dividerWidth = 0; } final MoreKeySpec[] moreKeys = key.getMoreKeys(); - mParams.setParameters(moreKeys.length, key.getMoreKeysColumn(), keyWidth, rowHeight, + mParams.setParameters(moreKeys.length, key.getMoreKeysColumnNumber(), keyWidth, rowHeight, key.getX() + key.getWidth() / 2, keyboard.mId.mWidth, - key.isFixedColumnOrderMoreKeys(), dividerWidth); + key.isMoreKeysFixedColumn(), key.isMoreKeysFixedOrder(), dividerWidth); } private static int getMaxKeyWidth(final Key parentKey, final int minKeyWidth, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java index cd29c8d17..5005b7d7d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java @@ -18,13 +18,9 @@ package com.android.inputmethod.keyboard.internal; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.content.Context; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.utils.CoordinateUtils; @@ -89,9 +85,9 @@ public final class KeyPreviewChoreographer { } final Object tag = keyPreviewView.getTag(); if (withAnimation) { - if (tag instanceof KeyPreviewAnimations) { - final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag; - animation.startDismiss(); + if (tag instanceof KeyPreviewAnimators) { + final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag; + animators.startDismiss(); return; } } @@ -161,87 +157,60 @@ public final class KeyPreviewChoreographer { } // Show preview with animation. - final Animator showUpAnimation = createShowUpAniation(key, keyPreviewView); - final Animator dismissAnimation = createDismissAnimation(key, keyPreviewView); - final KeyPreviewAnimations animation = new KeyPreviewAnimations( - showUpAnimation, dismissAnimation); - keyPreviewView.setTag(animation); - animation.startShowUp(); + final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView); + final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView); + final KeyPreviewAnimators animators = new KeyPreviewAnimators( + showUpAnimator, dismissAnimator); + keyPreviewView.setTag(animators); + animators.startShowUp(); } - private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f; - private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = - new AccelerateInterpolator(); - private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = - new DecelerateInterpolator(); - - private Animator createShowUpAniation(final Key key, final KeyPreviewView keyPreviewView) { - // TODO: Optimization for no scale animation and no duration. - final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_X, mParams.getShowUpStartScale(), - KEY_PREVIEW_SHOW_UP_END_SCALE); - final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_Y, mParams.getShowUpStartScale(), - KEY_PREVIEW_SHOW_UP_END_SCALE); - final AnimatorSet showUpAnimation = new AnimatorSet(); - showUpAnimation.play(scaleXAnimation).with(scaleYAnimation); - showUpAnimation.setDuration(mParams.getShowUpDuration()); - showUpAnimation.setInterpolator(DECELERATE_INTERPOLATOR); - showUpAnimation.addListener(new AnimatorListenerAdapter() { + public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) { + final Animator animator = mParams.createShowUpAnimator(keyPreviewView); + animator.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationStart(final Animator animation) { + public void onAnimationStart(final Animator animator) { showKeyPreview(key, keyPreviewView, false /* withAnimation */); } }); - return showUpAnimation; + return animator; } - private Animator createDismissAnimation(final Key key, final KeyPreviewView keyPreviewView) { - // TODO: Optimization for no scale animation and no duration. - final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_X, mParams.getDismissEndScale()); - final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_Y, mParams.getDismissEndScale()); - final AnimatorSet dismissAnimation = new AnimatorSet(); - dismissAnimation.play(scaleXAnimation).with(scaleYAnimation); - final int dismissDuration = Math.min( - mParams.getDismissDuration(), mParams.getLingerTimeout()); - dismissAnimation.setDuration(dismissDuration); - dismissAnimation.setInterpolator(ACCELERATE_INTERPOLATOR); - dismissAnimation.addListener(new AnimatorListenerAdapter() { + private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) { + final Animator animator = mParams.createDismissAnimator(keyPreviewView); + animator.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationEnd(final Animator animation) { + public void onAnimationEnd(final Animator animator) { dismissKeyPreview(key, false /* withAnimation */); } }); - return dismissAnimation; + return animator; } - private static class KeyPreviewAnimations extends AnimatorListenerAdapter { - private final Animator mShowUpAnimation; - private final Animator mDismissAnimation; + private static class KeyPreviewAnimators extends AnimatorListenerAdapter { + private final Animator mShowUpAnimator; + private final Animator mDismissAnimator; - public KeyPreviewAnimations(final Animator showUpAnimation, - final Animator dismissAnimation) { - mShowUpAnimation = showUpAnimation; - mDismissAnimation = dismissAnimation; + public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) { + mShowUpAnimator = showUpAnimator; + mDismissAnimator = dismissAnimator; } public void startShowUp() { - mShowUpAnimation.start(); + mShowUpAnimator.start(); } public void startDismiss() { - if (mShowUpAnimation.isRunning()) { - mShowUpAnimation.addListener(this); + if (mShowUpAnimator.isRunning()) { + mShowUpAnimator.addListener(this); return; } - mDismissAnimation.start(); + mDismissAnimator.start(); } @Override - public void onAnimationEnd(final Animator animation) { - mDismissAnimation.start(); + public void onAnimationEnd(final Animator animator) { + mDismissAnimator.start(); } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java index 68c9831fa..5ed39f986 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java @@ -16,8 +16,14 @@ package com.android.inputmethod.keyboard.internal; +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.res.TypedArray; import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; import com.android.inputmethod.latin.R; @@ -26,10 +32,15 @@ public final class KeyPreviewDrawParams { public final int mPreviewOffset; public final int mPreviewHeight; public final int mPreviewBackgroundResId; + private final int mShowUpAnimatorResId; + private final int mDismissAnimatorResId; + private boolean mHasCustomAnimationParams; private int mShowUpDuration; private int mDismissDuration; - private float mShowUpStartScale; - private float mDismissEndScale; + private float mShowUpStartXScale; + private float mShowUpStartYScale; + private float mDismissEndXScale; + private float mDismissEndYScale; private int mLingerTimeout; private boolean mShowPopup = true; @@ -67,6 +78,10 @@ public final class KeyPreviewDrawParams { R.styleable.MainKeyboardView_keyPreviewBackground, 0); mLingerTimeout = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0); + mShowUpAnimatorResId = mainKeyboardViewAttr.getResourceId( + R.styleable.MainKeyboardView_keyPreviewShowUpAnimator, 0); + mDismissAnimatorResId = mainKeyboardViewAttr.getResourceId( + R.styleable.MainKeyboardView_keyPreviewDismissAnimator, 0); } public void setVisibleOffset(final int previewVisibleOffset) { @@ -112,27 +127,62 @@ public final class KeyPreviewDrawParams { return mLingerTimeout; } - public void setAnimationParams(final float showUpStartScale, final int showUpDuration, - final float dismissEndScale, final int dismissDuration) { - mShowUpStartScale = showUpStartScale; + public void setAnimationParams(final boolean hasCustomAnimationParams, + final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration, + final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) { + mHasCustomAnimationParams = hasCustomAnimationParams; + mShowUpStartXScale = showUpStartXScale; + mShowUpStartYScale = showUpStartYScale; mShowUpDuration = showUpDuration; - mDismissEndScale = dismissEndScale; + mDismissEndXScale = dismissEndXScale; + mDismissEndYScale = dismissEndYScale; mDismissDuration = dismissDuration; } - public float getShowUpStartScale() { - return mShowUpStartScale; + private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f; + private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = + new AccelerateInterpolator(); + private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = + new DecelerateInterpolator(); + + public Animator createShowUpAnimator(final View target) { + if (mHasCustomAnimationParams) { + final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_X, mShowUpStartXScale, + KEY_PREVIEW_SHOW_UP_END_SCALE); + final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_Y, mShowUpStartYScale, + KEY_PREVIEW_SHOW_UP_END_SCALE); + final AnimatorSet showUpAnimator = new AnimatorSet(); + showUpAnimator.play(scaleXAnimator).with(scaleYAnimator); + showUpAnimator.setDuration(mShowUpDuration); + showUpAnimator.setInterpolator(DECELERATE_INTERPOLATOR); + return showUpAnimator; + } + final Animator animator = AnimatorInflater.loadAnimator( + target.getContext(), mShowUpAnimatorResId); + animator.setTarget(target); + animator.setInterpolator(DECELERATE_INTERPOLATOR); + return animator; } - public int getShowUpDuration() { - return mShowUpDuration; - } - - public float getDismissEndScale() { - return mDismissEndScale; - } - - public int getDismissDuration() { - return mDismissDuration; + public Animator createDismissAnimator(final View target) { + if (mHasCustomAnimationParams) { + final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_X, mDismissEndXScale); + final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_Y, mDismissEndYScale); + final AnimatorSet dismissAnimator = new AnimatorSet(); + dismissAnimator.play(scaleXAnimator).with(scaleYAnimator); + final int dismissDuration = Math.min(mDismissDuration, mLingerTimeout); + dismissAnimator.setDuration(dismissDuration); + dismissAnimator.setInterpolator(ACCELERATE_INTERPOLATOR); + return dismissAnimator; + } + final Animator animator = AnimatorInflater.loadAnimator( + target.getContext(), mDismissAnimatorResId); + animator.setTarget(target); + animator.setInterpolator(ACCELERATE_INTERPOLATOR); + return animator; } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 693e1cdcc..2e108756e 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -83,7 +83,6 @@ public final class BinaryDictionary extends Dictionary { public static final String DIR_NAME_SUFFIX_FOR_RECORD_MIGRATION = ".migrating"; private long mNativeDict; - private final Locale mLocale; private final long mDictSize; private final String mDictFilePath; private final boolean mUseFullEditDistance; @@ -117,8 +116,7 @@ public final class BinaryDictionary extends Dictionary { public BinaryDictionary(final String filename, final long offset, final long length, final boolean useFullEditDistance, final Locale locale, final String dictType, final boolean isUpdatable) { - super(dictType); - mLocale = locale; + super(dictType, locale); mDictSize = length; mDictFilePath = filename; mIsUpdatable = isUpdatable; @@ -138,8 +136,7 @@ public final class BinaryDictionary extends Dictionary { public BinaryDictionary(final String filename, final boolean useFullEditDistance, final Locale locale, final String dictType, final long formatVersion, final Map<String, String> attributeMap) { - super(dictType); - mLocale = locale; + super(dictType, locale); mDictSize = 0; mDictFilePath = filename; // On memory dictionary is always updatable. diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 729481326..02d5eddaa 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -57,6 +57,13 @@ public final class Constants { @SuppressWarnings("dep-ann") public static final String FORCE_ASCII = "forceAscii"; + /** + * The private IME option used to suppress the floating gesture preview for a given text + * field. This overrides the corresponding keyboard settings preference. + * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled} + */ + public static final String NO_FLOATING_GESTURE_PREVIEW = "noGestureFloatingPreview"; + private ImeOption() { // This utility class is not publicly instantiable. } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 560ced9c4..2f79c7662 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -16,12 +16,12 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; +import java.util.Locale; /** * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key @@ -62,9 +62,12 @@ public abstract class Dictionary { // Contextual dictionary. public static final String TYPE_CONTEXTUAL = "contextual"; public final String mDictType; + // The locale for this dictionary. May be null if unknown (phony dictionary for example). + public final Locale mLocale; - public Dictionary(final String dictType) { + public Dictionary(final String dictType, final Locale locale) { mDictType = dictType; + mLocale = locale; } /** @@ -162,7 +165,7 @@ public abstract class Dictionary { private static class PhonyDictionary extends Dictionary { // This class is not publicly instantiable. private PhonyDictionary(final String type) { - super(type); + super(type, null); } @Override diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 2b4c54d48..ca5e93714 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -25,6 +25,7 @@ import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -34,13 +35,14 @@ public final class DictionaryCollection extends Dictionary { private final String TAG = DictionaryCollection.class.getSimpleName(); protected final CopyOnWriteArrayList<Dictionary> mDictionaries; - public DictionaryCollection(final String dictType) { - super(dictType); + public DictionaryCollection(final String dictType, final Locale locale) { + super(dictType, locale); mDictionaries = new CopyOnWriteArrayList<>(); } - public DictionaryCollection(final String dictType, final Dictionary... dictionaries) { - super(dictType); + public DictionaryCollection(final String dictType, final Locale locale, + final Dictionary... dictionaries) { + super(dictType, locale); if (null == dictionaries) { mDictionaries = new CopyOnWriteArrayList<>(); } else { @@ -49,8 +51,9 @@ public final class DictionaryCollection extends Dictionary { } } - public DictionaryCollection(final String dictType, final Collection<Dictionary> dictionaries) { - super(dictType); + public DictionaryCollection(final String dictType, final Locale locale, + final Collection<Dictionary> dictionaries) { + super(dictType, locale); mDictionaries = new CopyOnWriteArrayList<>(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 60d6bc3e5..480bd1f85 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -33,6 +33,7 @@ import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.DistracterFilter; +import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary; import com.android.inputmethod.latin.utils.ExecutorUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; @@ -59,6 +60,7 @@ public class DictionaryFacilitator { // HACK: This threshold is being used when adding a capitalized entry in the User History // dictionary. private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; + private static final int MAX_DICTIONARY_FACILITATOR_CACHE_SIZE = 3; private Dictionaries mDictionaries = new Dictionaries(); private boolean mIsUserDictEnabled = false; @@ -66,6 +68,7 @@ public class DictionaryFacilitator { // To synchronize assigning mDictionaries to ensure closing dictionaries. private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; + private final DictionaryFacilitatorLruCache mFacilitatorCacheForPersonalization; private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS = new String[] { @@ -173,10 +176,14 @@ public class DictionaryFacilitator { public DictionaryFacilitator() { mDistracterFilter = DistracterFilter.EMPTY_DISTRACTER_FILTER; + mFacilitatorCacheForPersonalization = null; } - public DictionaryFacilitator(final DistracterFilter distracterFilter) { - mDistracterFilter = distracterFilter; + public DictionaryFacilitator(final Context context) { + mFacilitatorCacheForPersonalization = new DictionaryFacilitatorLruCache(context, + MAX_DICTIONARY_FACILITATOR_CACHE_SIZE, "" /* dictionaryNamePrefix */); + mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context, + mFacilitatorCacheForPersonalization); } public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { @@ -351,6 +358,9 @@ public class DictionaryFacilitator { for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { dictionaries.closeDict(dictType); } + if (mFacilitatorCacheForPersonalization != null) { + mFacilitatorCacheForPersonalization.evictAll(); + } mDistracterFilter.close(); } @@ -493,7 +503,7 @@ public class DictionaryFacilitator { final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final Dictionaries dictionaries = mDictionaries; final SuggestionResults suggestionResults = - new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); + new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); @@ -597,11 +607,15 @@ public class DictionaryFacilitator { } return; } + // TODO: Get locale from personalizationDataChunk.mDetectedLanguage. + final Locale dataChunkLocale = getLocale(); + final DictionaryFacilitator dictionaryFacilitatorForLocale = + mFacilitatorCacheForPersonalization.get(dataChunkLocale); final ArrayList<LanguageModelParam> languageModelParams = LanguageModelParam.createLanguageModelParamsFrom( personalizationDataChunk.mTokens, personalizationDataChunk.mTimestampInSeconds, - this /* dictionaryFacilitator */, spacingAndPunctuations, + dictionaryFacilitatorForLocale, spacingAndPunctuations, new DistracterFilterCheckingIsInDictionary( mDistracterFilter, personalizationDict)); if (languageModelParams == null || languageModelParams.isEmpty()) { diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 59de4f82a..3459b426d 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -50,7 +50,7 @@ public final class DictionaryFactory { final Locale locale, final boolean useFullEditDistance) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); - return new DictionaryCollection(Dictionary.TYPE_MAIN, + return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, createReadOnlyBinaryDictionary(context, locale)); } @@ -75,7 +75,7 @@ public final class DictionaryFactory { // If the list is empty, that means we should not use any dictionary (for example, the user // explicitly disabled the main dictionary), so the following is okay. dictList is never // null, but if for some reason it is, DictionaryCollection handles it gracefully. - return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList); + return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList); } /** @@ -188,7 +188,7 @@ public final class DictionaryFactory { public static Dictionary createDictionaryForTest(final AssetFileAddress[] dictionaryList, final boolean useFullEditDistance, Locale locale) { final DictionaryCollection dictionaryCollection = - new DictionaryCollection(Dictionary.TYPE_MAIN); + new DictionaryCollection(Dictionary.TYPE_MAIN, locale); for (final AssetFileAddress address : dictionaryList) { final ReadOnlyBinaryDictionary readOnlyBinaryDictionary = new ReadOnlyBinaryDictionary( address.mFilename, address.mOffset, address.mLength, useFullEditDistance, diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index de384037f..a1dd67f27 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -86,9 +86,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private final String mDictName; - /** Dictionary locale */ - private final Locale mLocale; - /** Dictionary file */ private final File mDictFile; @@ -137,10 +134,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ public ExpandableBinaryDictionary(final Context context, final String dictName, final Locale locale, final String dictType, final File dictFile) { - super(dictType); + super(dictType, locale); mDictName = dictName; mContext = context; - mLocale = locale; mDictFile = getDictFile(context, dictName, dictFile); mBinaryDictionary = null; mIsReloading = new AtomicBoolean(); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index ebe436128..782e18255 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; @@ -42,6 +43,12 @@ public final class InputAttributes { final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final public boolean mShouldShowVoiceInputKey; + /** + * Whether the floating gesture preview should be disabled. If true, this should override the + * corresponding keyboard settings preference, always suppressing the floating preview text. + * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled} + */ + final public boolean mDisableGestureFloatingPreviewText; final private int mInputType; final private EditorInfo mEditorInfo; final private String mPackageNameForPrivateImeOptions; @@ -76,6 +83,7 @@ public final class InputAttributes { mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; mShouldShowVoiceInputKey = false; + mDisableGestureFloatingPreviewText = false; return; } // inputClass == InputType.TYPE_CLASS_TEXT @@ -107,6 +115,9 @@ public final class InputAttributes { || hasNoMicrophoneKeyOption(); mShouldShowVoiceInputKey = !noMicrophone; + mDisableGestureFloatingPreviewText = InputAttributes.inPrivateImeOptions( + mPackageNameForPrivateImeOptions, NO_FLOATING_GESTURE_PREVIEW, editorInfo); + // If it's a browser edit field and auto correct is not ON explicitly, then // disable auto correction, but keep suggestions on. // If NO_SUGGESTIONS is set, don't do prediction. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ee0ff5c0a..c55acd462 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -93,6 +93,7 @@ import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import com.android.inputmethod.latin.utils.StatsUtils; +import com.android.inputmethod.latin.utils.StatsUtilsManager; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; @@ -130,8 +131,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final Settings mSettings; private final DictionaryFacilitator mDictionaryFacilitator = - new DictionaryFacilitator( - new DistracterFilterCheckingExactMatchesAndSuggestions(this /* context */)); + new DictionaryFacilitator(this /* context */); // TODO: Move from LatinIME. private final PersonalizationDictionaryUpdater mPersonalizationDictionaryUpdater = new PersonalizationDictionaryUpdater(this /* context */, mDictionaryFacilitator); @@ -158,6 +158,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); private final SpecialKeyDetector mSpecialKeyDetector; + private StatsUtilsManager mStatsUtilsManager; // Object for reacting to adding/removing a dictionary pack. private final BroadcastReceiver mDictionaryPackInstallReceiver = @@ -538,6 +539,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mSpecialKeyDetector = new SpecialKeyDetector(this); + mStatsUtilsManager = StatsUtilsManager.getInstance(); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled); @@ -553,8 +555,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen KeyboardSwitcher.init(this); AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); - StatsUtils.init(this); - + mStatsUtilsManager.onCreate(this /* context */); super.onCreate(); mHandler.onCreate(); @@ -586,7 +587,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter); DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this); - StatsUtils.onCreate(mSettings.getCurrent(), mRichImm); } @@ -609,7 +609,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList( true /* allowsImplicitlySelectedSubtypes */)); refreshPersonalizationDictionarySession(currentSettingsValues); - StatsUtils.onLoadSettings(currentSettingsValues); + mStatsUtilsManager.onLoadSettings(currentSettingsValues); } private void refreshPersonalizationDictionarySession( @@ -699,7 +699,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); unregisterReceiver(mDictionaryPackInstallReceiver); unregisterReceiver(mDictionaryDumpBroadcastReceiver); - StatsUtils.onDestroy(); + mStatsUtilsManager.onDestroy(); super.onDestroy(); } @@ -1179,10 +1179,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mInputLogic.getCurrentRecapitalizeState(); } - public Locale getCurrentSubtypeLocale() { - return mSubtypeSwitcher.getCurrentSubtypeLocale(); - } - /** * @param codePoints code points to get coordinates for. * @return x,y coordinates for this keyboard, as a flattened array. @@ -1496,7 +1492,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } final String wordToShow; if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) { - wordToShow = word.toLowerCase(getCurrentSubtypeLocale()); + wordToShow = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); } else { wordToShow = word; } diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index 5d4fc5861..ecf25c28b 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -40,7 +40,7 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { public ReadOnlyBinaryDictionary(final String filename, final long offset, final long length, final boolean useFullEditDistance, final Locale locale, final String dictType) { - super(dictType); + super(dictType, locale); mBinaryDictionary = new BinaryDictionary(filename, offset, length, useFullEditDistance, locale, dictType, false /* isUpdatable */); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 6779351fd..9e4aa40a2 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -84,7 +84,7 @@ public final class Suggest { private static ArrayList<SuggestedWordInfo> getTransformedSuggestedWordInfoList( final WordComposer wordComposer, final SuggestionResults results, - final int trailingSingleQuotesCount) { + final int trailingSingleQuotesCount, final Locale defaultLocale) { final boolean shouldMakeSuggestionsAllUpperCase = wordComposer.isAllUpperCase() && !wordComposer.isResumed(); final boolean isOnlyFirstCharCapitalized = @@ -96,9 +96,11 @@ public final class Suggest { || 0 != trailingSingleQuotesCount) { for (int i = 0; i < suggestionsCount; ++i) { final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + final Locale wordLocale = wordInfo.mSourceDict.mLocale; final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( - wordInfo, results.mLocale, shouldMakeSuggestionsAllUpperCase, - isOnlyFirstCharCapitalized, trailingSingleQuotesCount); + wordInfo, null == wordLocale ? defaultLocale : wordLocale, + shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized, + trailingSingleQuotesCount); suggestionsContainer.set(i, transformedWordInfo); } } @@ -134,7 +136,7 @@ public final class Suggest { SESSION_ID_TYPING); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, - trailingSingleQuotesCount); + trailingSingleQuotesCount, mDictionaryFacilitator.getLocale()); final boolean didRemoveTypedWord = SuggestedWordInfo.removeDups(wordComposer.getTypedWord(), suggestionsContainer); @@ -208,6 +210,7 @@ public final class Suggest { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, SESSION_ID_GESTURE); + final Locale defaultLocale = mDictionaryFacilitator.getLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); @@ -216,9 +219,10 @@ public final class Suggest { if (isFirstCharCapitalized || isAllUpperCase) { for (int i = 0; i < suggestionsCount; ++i) { final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + final Locale wordlocale = wordInfo.mSourceDict.mLocale; final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( - wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized, - 0 /* trailingSingleQuotesCount */); + wordInfo, null == wordlocale ? defaultLocale : wordlocale, isAllUpperCase, + isFirstCharCapitalized, 0 /* trailingSingleQuotesCount */); suggestionsContainer.set(i, transformedWordInfo); } } diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 1eebabece..6bc3da885 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -429,7 +429,7 @@ public class SuggestedWords { */ @UsedForTesting public SuggestedWordInfo getTypedWordInfoOrNull() { - if (this == EMPTY) { + if (SuggestedWords.INDEX_OF_TYPED_WORD >= size()) { return null; } final SuggestedWordInfo info = getInfo(SuggestedWords.INDEX_OF_TYPED_WORD); diff --git a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java index e4ee42660..123ab208c 100644 --- a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java +++ b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java @@ -17,21 +17,16 @@ package com.android.inputmethod.latin; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.os.Process; -import android.preference.PreferenceManager; import android.util.Log; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.IntentCompatUtils; -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; -import com.android.inputmethod.latin.setup.SetupActivity; import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; /** @@ -58,6 +53,9 @@ import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; * When a multiuser account has been created, {@link Intent#ACTION_USER_INITIALIZE} is received * by this receiver and it checks the whether the setup wizard's icon should be appeared or not on * the launcher depending on which partition this IME is installed. + * + * When the system locale has been changed, {@link Intent#ACTION_LOCALE_CHANGED} is received by + * this receiver and the {@link KeyboardLayoutSet}'s cache is cleared. */ public final class SystemBroadcastReceiver extends BroadcastReceiver { private static final String TAG = SystemBroadcastReceiver.class.getSimpleName(); @@ -67,21 +65,22 @@ public final class SystemBroadcastReceiver extends BroadcastReceiver { final String intentAction = intent.getAction(); if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) { Log.i(TAG, "Package has been replaced: " + context.getPackageName()); - } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { - Log.i(TAG, "Boot has been completed"); - } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) { - Log.i(TAG, "User initialize"); - } - - LauncherIconVisibilityManager.onReceiveGlobalIntent(intentAction, context); - - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) { // Need to restore additional subtypes because system always clears additional // subtypes when the package is replaced. RichInputMethodManager.init(context); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes(context); richImm.setAdditionalInputMethodSubtypes(additionalSubtypes); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { + Log.i(TAG, "Boot has been completed"); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) { + Log.i(TAG, "User initialize"); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) { + Log.i(TAG, "System locale changed"); + KeyboardLayoutSet.onSystemLocaleChanged(); } // The process that hosts this broadcast receiver is invoked and remains alive even after diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index 6d7f53cf0..d53a61654 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -30,11 +30,14 @@ import android.preference.DialogPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; +import android.support.v4.view.ViewCompat; import android.util.Pair; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.widget.ArrayAdapter; @@ -43,6 +46,7 @@ import android.widget.SpinnerAdapter; import android.widget.Toast; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; @@ -233,6 +237,12 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); + // All keyboard layout names are in the Latin script and thus left to right. That means + // the view would align them to the left even if the system locale is RTL, but that + // would look strange. To fix this, we align them to the view's start, which will be + // natural for any direction. + ViewCompatUtils.setTextAlignment( + mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); return v; } @@ -398,6 +408,16 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + final View view = super.onCreateView(inflater, container, savedInstanceState); + // For correct display in RTL locales, we need to set the layout direction of the + // fragment's top view. + ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE); + return view; + } + + @Override public void onActivityCreated(final Bundle savedInstanceState) { final Context context = getActivity(); mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context); @@ -422,7 +442,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { KEY_SUBTYPE_FOR_SUBTYPE_ENABLER); final SubtypePreference subtypePref = (SubtypePreference)findPreference( mSubtypePreferenceKeyForSubtypeEnabler); - mSubtypeEnablerNotificationDialog = createDialog(subtypePref); + mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); } } @@ -476,7 +496,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); - mSubtypeEnablerNotificationDialog = createDialog(subtypePref); + mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); return; } @@ -513,7 +533,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { localeString, keyboardLayoutSetName); } - private AlertDialog createDialog(final SubtypePreference subtypePref) { + private AlertDialog createDialog() { final AlertDialog.Builder builder = new AlertDialog.Builder( DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index 63d848e2d..48f4c758c 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -23,10 +23,16 @@ public final class DebugSettings { "force_physical_keyboard_special_key"; public static final String PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD = "pref_show_ui_to_accept_typed_word"; - public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = - "pref_key_preview_show_up_start_scale"; - public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = - "pref_key_preview_dismiss_end_scale"; + public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS = + "pref_has_custom_key_preview_animation_params"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE = + "pref_key_preview_show_up_start_x_scale"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE = + "pref_key_preview_show_up_start_y_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE = + "pref_key_preview_dismiss_end_x_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE = + "pref_key_preview_dismiss_end_y_scale"; public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION = "pref_key_preview_show_up_duration"; public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java index dc2f88aa8..5640e2039 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -78,12 +78,18 @@ public final class DebugSettingsFragment extends SubScreenFragment res.getInteger(R.integer.config_key_preview_show_up_duration)); setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, res.getInteger(R.integer.config_key_preview_dismiss_duration)); - setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_show_up_start_scale)); - setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_dismiss_end_scale)); + final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale); + final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + defaultKeyPreviewShowUpStartScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + defaultKeyPreviewShowUpStartScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + defaultKeyPreviewDismissEndScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + defaultKeyPreviewDismissEndScale); mServiceNeedsRestart = false; mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE); diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 91a2b6776..11291959c 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -104,10 +104,13 @@ public class SettingsValues { // Debug settings public final boolean mIsInternal; + public final boolean mHasCustomKeyPreviewAnimationParams; public final int mKeyPreviewShowUpDuration; public final int mKeyPreviewDismissDuration; - public final float mKeyPreviewShowUpStartScale; - public final float mKeyPreviewDismissEndScale; + public final float mKeyPreviewShowUpStartXScale; + public final float mKeyPreviewShowUpStartYScale; + public final float mKeyPreviewDismissEndXScale; + public final float mKeyPreviewDismissEndYScale; public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, final InputAttributes inputAttributes) { @@ -162,8 +165,8 @@ public class SettingsValues { autoCorrectionThresholdRawValue); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); - mGestureFloatingPreviewTextEnabled = prefs.getBoolean( - Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); + mGestureFloatingPreviewTextEnabled = !mInputAttributes.mDisableGestureFloatingPreviewText + && prefs.getBoolean(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res); mAutoCorrectionEnabledPerUserSettings = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; @@ -179,20 +182,30 @@ public class SettingsValues { mTextHighlightColorForAddToDictionaryIndicator = res.getColor( R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color); mIsInternal = Settings.isInternal(prefs); + mHasCustomKeyPreviewAnimationParams = prefs.getBoolean( + DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false); mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, res.getInteger(R.integer.config_key_preview_dismiss_duration)); - mKeyPreviewShowUpStartScale = Settings.readKeyPreviewAnimationScale( - prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_show_up_start_scale)); - mKeyPreviewDismissEndScale = Settings.readKeyPreviewAnimationScale( - prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_dismiss_end_scale)); + final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale); + final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale); + mKeyPreviewShowUpStartXScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + defaultKeyPreviewShowUpStartScale); + mKeyPreviewShowUpStartYScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + defaultKeyPreviewShowUpStartScale); + mKeyPreviewDismissEndXScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + defaultKeyPreviewDismissEndScale); + mKeyPreviewDismissEndYScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + defaultKeyPreviewDismissEndScale); mDisplayOrientation = res.getConfiguration().orientation; mAppWorkarounds = new AsyncResultHolder<>(); final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo( @@ -429,10 +442,14 @@ public class SettingsValues { sb.append("" + mKeyPreviewShowUpDuration); sb.append("\n mKeyPreviewDismissDuration = "); sb.append("" + mKeyPreviewDismissDuration); - sb.append("\n mKeyPreviewShowUpStartScale = "); - sb.append("" + mKeyPreviewShowUpStartScale); - sb.append("\n mKeyPreviewDismissEndScale = "); - sb.append("" + mKeyPreviewDismissEndScale); + sb.append("\n mKeyPreviewShowUpStartScaleX = "); + sb.append("" + mKeyPreviewShowUpStartXScale); + sb.append("\n mKeyPreviewShowUpStartScaleY = "); + sb.append("" + mKeyPreviewShowUpStartYScale); + sb.append("\n mKeyPreviewDismissEndScaleX = "); + sb.append("" + mKeyPreviewDismissEndXScale); + sb.append("\n mKeyPreviewDismissEndScaleY = "); + sb.append("" + mKeyPreviewDismissEndYScale); return sb.toString(); } } diff --git a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java index 9585736e7..3f0b10225 100644 --- a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java +++ b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java @@ -24,7 +24,6 @@ import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.util.Log; -import com.android.inputmethod.compat.IntentCompatUtils; import com.android.inputmethod.latin.settings.Settings; /** @@ -55,14 +54,6 @@ import com.android.inputmethod.latin.settings.Settings; public final class LauncherIconVisibilityManager { private static final String TAG = LauncherIconVisibilityManager.class.getSimpleName(); - public static void onReceiveGlobalIntent(final String action, final Context context) { - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action) || - Intent.ACTION_BOOT_COMPLETED.equals(action) || - IntentCompatUtils.is_ACTION_USER_INITIALIZE(action)) { - updateSetupWizardIconVisibility(context); - } - } - public static void updateSetupWizardIconVisibility(final Context context) { final ComponentName setupWizardActivity = new ComponentName(context, SetupActivity.class); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 2207ffea9..e10571e4a 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -20,13 +20,14 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.ConcurrentHashMap; import android.content.Context; import android.content.res.Resources; import android.text.InputType; import android.util.Log; import android.util.LruCache; +import android.util.Pair; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; @@ -34,6 +35,7 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.DictionaryFacilitatorLruCache; import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -49,15 +51,15 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr DistracterFilterCheckingExactMatchesAndSuggestions.class.getSimpleName(); private static final boolean DEBUG = false; - private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; - private static final int MAX_DISTRACTERS_CACHE_SIZE = 512; + private static final int MAX_DISTRACTERS_CACHE_SIZE = 1024; private final Context mContext; - private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; - private final Map<Locale, Keyboard> mLocaleToKeyboardMap; - private final DictionaryFacilitator mDictionaryFacilitator; - private final LruCache<String, Boolean> mDistractersCache; - private Keyboard mKeyboard; + private final ConcurrentHashMap<Locale, InputMethodSubtype> mLocaleToSubtypeCache; + private final ConcurrentHashMap<Locale, Keyboard> mLocaleToKeyboardCache; + private final DictionaryFacilitatorLruCache mDictionaryFacilitatorLruCache; + // The key is a pair of a locale and a word. The value indicates the word is a distracter to + // words of the locale. + private final LruCache<Pair<Locale, String>, Boolean> mDistractersCache; private final Object mLock = new Object(); // If the score of the top suggestion exceeds this value, the tested word (e.g., @@ -71,19 +73,23 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr * Create a DistracterFilter instance. * * @param context the context. + * @param dictionaryFacilitatorLruCache the cache of dictionaryFacilitators that are used for + * checking distracters. */ - public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context) { + public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context, + final DictionaryFacilitatorLruCache dictionaryFacilitatorLruCache) { mContext = context; - mLocaleToSubtypeMap = new HashMap<>(); - mLocaleToKeyboardMap = new HashMap<>(); - mDictionaryFacilitator = new DictionaryFacilitator(); + mLocaleToSubtypeCache = new ConcurrentHashMap<>(); + mLocaleToKeyboardCache = new ConcurrentHashMap<>(); + mDictionaryFacilitatorLruCache = dictionaryFacilitatorLruCache; mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE); - mKeyboard = null; } @Override public void close() { - mDictionaryFacilitator.closeDictionaries(); + mLocaleToSubtypeCache.clear(); + mLocaleToKeyboardCache.clear(); + mDistractersCache.evictAll(); } @Override @@ -100,29 +106,36 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr newLocaleToSubtypeMap.put(locale, subtype); } } - if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { + if (mLocaleToSubtypeCache.equals(newLocaleToSubtypeMap)) { // Enabled subtypes have not been changed. return; } - synchronized (mLock) { - mLocaleToSubtypeMap.clear(); - mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); - mLocaleToKeyboardMap.clear(); + // Update subtype and keyboard map for locales that are in the current mapping. + for (final Locale locale: mLocaleToSubtypeCache.keySet()) { + if (newLocaleToSubtypeMap.containsKey(locale)) { + final InputMethodSubtype newSubtype = newLocaleToSubtypeMap.remove(locale); + if (newSubtype.equals(newLocaleToSubtypeMap.get(locale))) { + // Mapping has not been changed. + continue; + } + mLocaleToSubtypeCache.replace(locale, newSubtype); + } else { + mLocaleToSubtypeCache.remove(locale); + } + mLocaleToKeyboardCache.remove(locale); } + // Add locales that are not in the current mapping. + mLocaleToSubtypeCache.putAll(newLocaleToSubtypeMap); } - private void loadKeyboardForLocale(final Locale newLocale) { - final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); + private Keyboard getKeyboardForLocale(final Locale locale) { + final Keyboard cachedKeyboard = mLocaleToKeyboardCache.get(locale); if (cachedKeyboard != null) { - mKeyboard = cachedKeyboard; - return; - } - final InputMethodSubtype subtype; - synchronized (mLock) { - subtype = mLocaleToSubtypeMap.get(newLocale); + return cachedKeyboard; } + final InputMethodSubtype subtype = mLocaleToSubtypeCache.get(locale); if (subtype == null) { - return; + return null; } final EditorInfo editorInfo = new EditorInfo(); editorInfo.inputType = InputType.TYPE_CLASS_TEXT; @@ -135,15 +148,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr builder.setSubtype(new RichInputMethodSubtype(subtype)); builder.setIsSpellChecker(false /* isSpellChecker */); final KeyboardLayoutSet layoutSet = builder.build(); - mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); - } - - private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { - mDictionaryFacilitator.resetDictionaries(mContext, newlocale, - false /* useContactsDict */, false /* usePersonalizedDicts */, - false /* forceReloadMainDictionary */, null /* listener */); - mDictionaryFacilitator.waitForLoadingMainDictionary( - TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); + final Keyboard newKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); + mLocaleToKeyboardCache.put(locale, newKeyboard); + return newKeyboard; } /** @@ -161,30 +168,18 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr if (locale == null) { return false; } - if (!locale.equals(mDictionaryFacilitator.getLocale())) { - synchronized (mLock) { - if (!mLocaleToSubtypeMap.containsKey(locale)) { - Log.e(TAG, "Locale " + locale + " is not enabled."); - // TODO: Investigate what we should do for disabled locales. - return false; - } - loadKeyboardForLocale(locale); - // Reset dictionaries for the locale. - try { - mDistractersCache.evictAll(); - loadDictionariesForLocale(locale); - } catch (final InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", - e); - return false; - } - } + if (!mLocaleToSubtypeCache.containsKey(locale)) { + Log.e(TAG, "Locale " + locale + " is not enabled."); + // TODO: Investigate what we should do for disabled locales. + return false; } - + final DictionaryFacilitator dictionaryFacilitator = + mDictionaryFacilitatorLruCache.get(locale); if (DEBUG) { Log.d(TAG, "testedWord: " + testedWord); } - final Boolean isCachedDistracter = mDistractersCache.get(testedWord); + final Pair<Locale, String> cacheKey = new Pair<>(locale, testedWord); + final Boolean isCachedDistracter = mDistractersCache.get(cacheKey); if (isCachedDistracter != null && isCachedDistracter) { if (DEBUG) { Log.d(TAG, "isDistracter: true (cache hit)"); @@ -193,13 +188,13 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr } final boolean isDistracterCheckedByGetMaxFreqencyOfExactMatches = - checkDistracterUsingMaxFreqencyOfExactMatches(testedWord); + checkDistracterUsingMaxFreqencyOfExactMatches(dictionaryFacilitator, testedWord); if (isDistracterCheckedByGetMaxFreqencyOfExactMatches) { - // Add the word to the cache. - mDistractersCache.put(testedWord, Boolean.TRUE); + // Add the pair of locale and word to the cache. + mDistractersCache.put(cacheKey, Boolean.TRUE); return true; } - final boolean isValidWord = mDictionaryFacilitator.isValidWord(testedWord, + final boolean isValidWord = dictionaryFacilitator.isValidWord(testedWord, false /* ignoreCase */); if (isValidWord) { // Valid word is not a distractor. @@ -209,21 +204,23 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return false; } + final Keyboard keyboard = getKeyboardForLocale(locale); final boolean isDistracterCheckedByGetSuggestion = - checkDistracterUsingGetSuggestions(testedWord); + checkDistracterUsingGetSuggestions(dictionaryFacilitator, keyboard, testedWord); if (isDistracterCheckedByGetSuggestion) { - // Add the word to the cache. - mDistractersCache.put(testedWord, Boolean.TRUE); + // Add the pair of locale and word to the cache. + mDistractersCache.put(cacheKey, Boolean.TRUE); return true; } return false; } - private boolean checkDistracterUsingMaxFreqencyOfExactMatches(final String testedWord) { + private static boolean checkDistracterUsingMaxFreqencyOfExactMatches( + final DictionaryFacilitator dictionaryFacilitator, final String testedWord) { // The tested word is a distracter when there is a word that is exact matched to the tested // word and its probability is higher than the tested word's probability. - final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); - final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); + final int perfectMatchFreq = dictionaryFacilitator.getFrequency(testedWord); + final int exactMatchFreq = dictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); final boolean isDistracter = perfectMatchFreq < exactMatchFreq; if (DEBUG) { Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); @@ -233,8 +230,10 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return isDistracter; } - private boolean checkDistracterUsingGetSuggestions(final String testedWord) { - if (mKeyboard == null) { + private boolean checkDistracterUsingGetSuggestions( + final DictionaryFacilitator dictionaryFacilitator, final Keyboard keyboard, + final String testedWord) { + if (keyboard == null) { return false; } final SettingsValuesForSuggestion settingsValuesForSuggestion = @@ -247,24 +246,24 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr testedWord; final WordComposer composer = new WordComposer(); final int[] codePoints = StringUtils.toCodePointArray(testedWord); - + final int[] coordinates = keyboard.getCoordinates(codePoints); + composer.setComposingWord(codePoints, coordinates); + final SuggestionResults suggestionResults; synchronized (mLock) { - final int[] coordinates = mKeyboard.getCoordinates(codePoints); - composer.setComposingWord(codePoints, coordinates); - final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, mKeyboard.getProximityInfo(), + suggestionResults = dictionaryFacilitator.getSuggestionResults( + composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(), settingsValuesForSuggestion, 0 /* sessionId */); - if (suggestionResults.isEmpty()) { - return false; - } - final SuggestedWordInfo firstSuggestion = suggestionResults.first(); - final boolean isDistractor = suggestionExceedsDistracterThreshold( - firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD); - if (DEBUG) { - Log.d(TAG, "isDistracter: " + isDistractor); - } - return isDistractor; } + if (suggestionResults.isEmpty()) { + return false; + } + final SuggestedWordInfo firstSuggestion = suggestionResults.first(); + final boolean isDistractor = suggestionExceedsDistracterThreshold( + firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD); + if (DEBUG) { + Log.d(TAG, "isDistracter: " + isDistractor); + } + return isDistractor; } private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion, diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index 7170bd789..eaa5743d4 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -30,18 +30,16 @@ import java.util.TreeSet; * than its limit */ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { - public final Locale mLocale; public final ArrayList<SuggestedWordInfo> mRawSuggestions; private final int mCapacity; - public SuggestionResults(final Locale locale, final int capacity) { - this(locale, sSuggestedWordInfoComparator, capacity); + public SuggestionResults(final int capacity) { + this(sSuggestedWordInfoComparator, capacity); } - public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator, + public SuggestionResults(final Comparator<SuggestedWordInfo> comparator, final int capacity) { super(comparator); - mLocale = locale; mCapacity = capacity; if (ProductionFlags.INCLUDE_RAW_SUGGESTIONS) { mRawSuggestions = new ArrayList<>(); |