aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r--java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java186
-rw-r--r--java/src/com/android/inputmethod/compat/ViewCompatUtils.java7
-rw-r--r--java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java42
-rw-r--r--java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java72
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java86
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardTheme.java18
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecorator.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java4
-rw-r--r--java/src/com/android/inputmethod/latin/BackupAgent.java30
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java32
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java16
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java6
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java7
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java48
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java3
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodManager.java20
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java22
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java60
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java25
-rw-r--r--java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java47
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java44
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java5
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/WordProperty.java17
-rw-r--r--java/src/com/android/inputmethod/latin/network/AuthException.java35
-rw-r--r--java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java51
-rw-r--r--java/src/com/android/inputmethod/latin/network/HttpException.java46
-rw-r--r--java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java34
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java153
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java40
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java358
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java449
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettings.java25
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java40
-rw-r--r--java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java60
-rw-r--r--java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java1
-rw-r--r--java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java9
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java8
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java1
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java4
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java4
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java36
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java6
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java34
-rw-r--r--java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java31
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java21
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java11
50 files changed, 1395 insertions, 896 deletions
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
index 5af31795c..f4f54b624 100644
--- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
@@ -16,13 +16,20 @@
package com.android.inputmethod.compat;
+import android.annotation.TargetApi;
import android.graphics.Matrix;
import android.graphics.RectF;
+import android.os.Build;
+import android.view.inputmethod.CursorAnchorInfo;
-import com.android.inputmethod.annotations.UsedForTesting;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
-@UsedForTesting
-public final class CursorAnchorInfoCompatWrapper {
+/**
+ * A wrapper for {@link CursorAnchorInfo}, which has been introduced in API Level 21. You can use
+ * this wrapper to avoid direct dependency on newly introduced types.
+ */
+public class CursorAnchorInfoCompatWrapper {
/**
* The insertion marker or character bounds have at least one visible region.
@@ -39,123 +46,138 @@ public final class CursorAnchorInfoCompatWrapper {
*/
public static final int FLAG_IS_RTL = 0x04;
- // Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX).
- private static final CompatUtils.ClassWrapper sCursorAnchorInfoClass;
- private static final CompatUtils.ToIntMethodWrapper sGetSelectionStartMethod;
- private static final CompatUtils.ToIntMethodWrapper sGetSelectionEndMethod;
- private static final CompatUtils.ToObjectMethodWrapper<RectF> sGetCharacterBoundsMethod;
- private static final CompatUtils.ToIntMethodWrapper sGetCharacterBoundsFlagsMethod;
- private static final CompatUtils.ToObjectMethodWrapper<CharSequence> sGetComposingTextMethod;
- private static final CompatUtils.ToIntMethodWrapper sGetComposingTextStartMethod;
- private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBaselineMethod;
- private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBottomMethod;
- private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerHorizontalMethod;
- private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerTopMethod;
- private static final CompatUtils.ToObjectMethodWrapper<Matrix> sGetMatrixMethod;
- private static final CompatUtils.ToIntMethodWrapper sGetInsertionMarkerFlagsMethod;
-
- private static int INVALID_TEXT_INDEX = -1;
- static {
- sCursorAnchorInfoClass = CompatUtils.getClassWrapper(
- "android.view.inputmethod.CursorAnchorInfo");
- sGetSelectionStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getSelectionStart", INVALID_TEXT_INDEX);
- sGetSelectionEndMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getSelectionEnd", INVALID_TEXT_INDEX);
- sGetCharacterBoundsMethod = sCursorAnchorInfoClass.getMethod(
- "getCharacterBounds", (RectF)null, int.class);
- sGetCharacterBoundsFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getCharacterBoundsFlags", 0, int.class);
- sGetComposingTextMethod = sCursorAnchorInfoClass.getMethod(
- "getComposingText", (CharSequence)null);
- sGetComposingTextStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getComposingTextStart", INVALID_TEXT_INDEX);
- sGetInsertionMarkerBaselineMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getInsertionMarkerBaseline", 0.0f);
- sGetInsertionMarkerBottomMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getInsertionMarkerBottom", 0.0f);
- sGetInsertionMarkerHorizontalMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getInsertionMarkerHorizontal", 0.0f);
- sGetInsertionMarkerTopMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getInsertionMarkerTop", 0.0f);
- sGetMatrixMethod = sCursorAnchorInfoClass.getMethod("getMatrix", (Matrix)null);
- sGetInsertionMarkerFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getInsertionMarkerFlags", 0);
- }
-
- @UsedForTesting
- public boolean isAvailable() {
- return sCursorAnchorInfoClass.exists() && mInstance != null;
- }
-
- private Object mInstance;
-
- private CursorAnchorInfoCompatWrapper(final Object instance) {
- mInstance = instance;
+ private CursorAnchorInfoCompatWrapper() {
+ // This class is not publicly instantiable.
}
- @UsedForTesting
- public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) {
- if (!sCursorAnchorInfoClass.exists()) {
- return new CursorAnchorInfoCompatWrapper(null);
+ @TargetApi(BuildCompatUtils.VERSION_CODES_LXX)
+ @Nullable
+ public static CursorAnchorInfoCompatWrapper wrap(@Nullable final CursorAnchorInfo instance) {
+ if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) {
+ return null;
}
- return new CursorAnchorInfoCompatWrapper(instance);
- }
-
- private static final class FakeHolder {
- static CursorAnchorInfoCompatWrapper sInstance = new CursorAnchorInfoCompatWrapper(null);
- }
-
- @UsedForTesting
- public static CursorAnchorInfoCompatWrapper getFake() {
- return FakeHolder.sInstance;
+ if (instance == null) {
+ return null;
+ }
+ return new RealWrapper(instance);
}
public int getSelectionStart() {
- return sGetSelectionStartMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public int getSelectionEnd() {
- return sGetSelectionEndMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public CharSequence getComposingText() {
- return sGetComposingTextMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public int getComposingTextStart() {
- return sGetComposingTextStartMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public Matrix getMatrix() {
- return sGetMatrixMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public RectF getCharacterBounds(final int index) {
- return sGetCharacterBoundsMethod.invoke(mInstance, index);
+ throw new UnsupportedOperationException("not supported.");
}
public int getCharacterBoundsFlags(final int index) {
- return sGetCharacterBoundsFlagsMethod.invoke(mInstance, index);
+ throw new UnsupportedOperationException("not supported.");
}
public float getInsertionMarkerBaseline() {
- return sGetInsertionMarkerBaselineMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public float getInsertionMarkerBottom() {
- return sGetInsertionMarkerBottomMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public float getInsertionMarkerHorizontal() {
- return sGetInsertionMarkerHorizontalMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public float getInsertionMarkerTop() {
- return sGetInsertionMarkerTopMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
}
public int getInsertionMarkerFlags() {
- return sGetInsertionMarkerFlagsMethod.invoke(mInstance);
+ throw new UnsupportedOperationException("not supported.");
+ }
+
+ @TargetApi(BuildCompatUtils.VERSION_CODES_LXX)
+ private static final class RealWrapper extends CursorAnchorInfoCompatWrapper {
+
+ @Nonnull
+ private final CursorAnchorInfo mInstance;
+
+ public RealWrapper(@Nonnull final CursorAnchorInfo info) {
+ mInstance = info;
+ }
+
+ @Override
+ public int getSelectionStart() {
+ return mInstance.getSelectionStart();
+ }
+
+ @Override
+ public int getSelectionEnd() {
+ return mInstance.getSelectionEnd();
+ }
+
+ @Override
+ public CharSequence getComposingText() {
+ return mInstance.getComposingText();
+ }
+
+ @Override
+ public int getComposingTextStart() {
+ return mInstance.getComposingTextStart();
+ }
+
+ @Override
+ public Matrix getMatrix() {
+ return mInstance.getMatrix();
+ }
+
+ @Override
+ public RectF getCharacterBounds(final int index) {
+ return mInstance.getCharacterBounds(index);
+ }
+
+ @Override
+ public int getCharacterBoundsFlags(final int index) {
+ return mInstance.getCharacterBoundsFlags(index);
+ }
+
+ @Override
+ public float getInsertionMarkerBaseline() {
+ return mInstance.getInsertionMarkerBaseline();
+ }
+
+ @Override
+ public float getInsertionMarkerBottom() {
+ return mInstance.getInsertionMarkerBottom();
+ }
+
+ @Override
+ public float getInsertionMarkerHorizontal() {
+ return mInstance.getInsertionMarkerHorizontal();
+ }
+
+ @Override
+ public float getInsertionMarkerTop() {
+ return mInstance.getInsertionMarkerTop();
+ }
+
+ @Override
+ public int getInsertionMarkerFlags() {
+ return mInstance.getInsertionMarkerFlags();
+ }
}
}
diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
index 0f00be133..16260ab6a 100644
--- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
@@ -31,9 +31,6 @@ public final class ViewCompatUtils {
private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod(
View.class, "setPaddingRelative",
int.class, int.class, int.class, int.class);
- // 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);
@@ -58,10 +55,6 @@ public final class ViewCompatUtils {
CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom);
}
- public static void setElevation(final View view, final float elevation) {
- 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;
diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java
new file mode 100644
index 000000000..52b8b74e8
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.inputmethodservice.InputMethodService;
+import android.view.View;
+
+public class ViewOutlineProviderCompatUtils {
+ private ViewOutlineProviderCompatUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public interface InsetsUpdater {
+ public void setInsets(final InputMethodService.Insets insets);
+ }
+
+ private static final InsetsUpdater EMPTY_INSETS_UPDATER = new InsetsUpdater() {
+ @Override
+ public void setInsets(final InputMethodService.Insets insets) {}
+ };
+
+ public static InsetsUpdater setInsetsOutlineProvider(final View view) {
+ if (BuildCompatUtils.EFFECTIVE_SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) {
+ return EMPTY_INSETS_UPDATER;
+ }
+ return ViewOutlineProviderCompatUtilsLXX.setInsetsOutlineProvider(view);
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java
new file mode 100644
index 000000000..5bbb5ce99
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.annotation.TargetApi;
+import android.graphics.Outline;
+import android.inputmethodservice.InputMethodService;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+class ViewOutlineProviderCompatUtilsLXX {
+ private ViewOutlineProviderCompatUtilsLXX() {
+ // This utility class is not publicly instantiable.
+ }
+
+ static InsetsUpdater setInsetsOutlineProvider(final View view) {
+ final InsetsOutlineProvider provider = new InsetsOutlineProvider(view);
+ view.setOutlineProvider(provider);
+ return provider;
+ }
+
+ private static class InsetsOutlineProvider extends ViewOutlineProvider
+ implements InsetsUpdater {
+ private final View mView;
+ private static final int NO_DATA = -1;
+ private int mLastVisibleTopInsets = NO_DATA;
+
+ public InsetsOutlineProvider(final View view) {
+ mView = view;
+ view.setOutlineProvider(this);
+ }
+
+ @Override
+ public void setInsets(final InputMethodService.Insets insets) {
+ final int visibleTopInsets = insets.visibleTopInsets;
+ if (mLastVisibleTopInsets != visibleTopInsets) {
+ mLastVisibleTopInsets = visibleTopInsets;
+ mView.invalidateOutline();
+ }
+ }
+
+ @Override
+ public void getOutline(final View view, final Outline outline) {
+ if (mLastVisibleTopInsets == NO_DATA) {
+ // Call default implementation.
+ ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
+ return;
+ }
+ // TODO: Revisit this when floating/resize keyboard is supported.
+ outline.setRect(
+ view.getLeft(), mLastVisibleTopInsets, view.getRight(), view.getBottom());
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 52b9284be..1c66c37d3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -28,6 +28,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.EditorInfoCompatUtils;
import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
@@ -39,6 +40,7 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodSubtype;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.define.DebugFlags;
+import com.android.inputmethod.latin.utils.DebugLogUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
@@ -313,23 +315,78 @@ public final class KeyboardLayoutSet {
return this;
}
+ private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
+ new HashMap<>();
+ public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) {
+ final Integer value = sScriptIdsForSubtypes.get(subtype);
+ if (null == value) {
+ final int scriptId = readScriptId(resources, subtype);
+ sScriptIdsForSubtypes.put(subtype, scriptId);
+ return scriptId;
+ }
+ return value;
+ }
+
+ // Super redux version of reading the script ID for some subtype from Xml.
+ private static int readScriptId(final Resources resources,
+ final InputMethodSubtype subtype) {
+ final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
+ + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+ final int xmlId = getXmlId(resources, layoutSetName);
+ final XmlResourceParser parser = resources.getXml(xmlId);
+ try {
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ // Bovinate through the XML stupidly searching for TAG_FEATURE, and read
+ // the script Id from it.
+ parser.next();
+ final String tag = parser.getName();
+ if (TAG_FEATURE.equals(tag)) {
+ return readScriptIdFromTagFeature(resources, parser);
+ }
+ }
+ } catch (final IOException | XmlPullParserException e) {
+ throw new RuntimeException(e.getMessage() + " in " + layoutSetName, e);
+ } finally {
+ parser.close();
+ }
+ // If the tag is not found, then the default script is Latin.
+ return ScriptUtils.SCRIPT_LATIN;
+ }
+
+ private static int readScriptIdFromTagFeature(final Resources resources,
+ final XmlPullParser parser) throws IOException, XmlPullParserException {
+ final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.KeyboardLayoutSet_Feature);
+ try {
+ final int scriptId =
+ featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript,
+ ScriptUtils.SCRIPT_UNKNOWN);
+ XmlParseUtils.checkEndTag(TAG_FEATURE, parser);
+ return scriptId;
+ } finally {
+ featureAttr.recycle();
+ }
+ }
+
public KeyboardLayoutSet build() {
if (mParams.mSubtype == null)
throw new RuntimeException("KeyboardLayoutSet subtype is not specified");
- final String packageName = mResources.getResourcePackageName(
- R.xml.keyboard_layout_set_qwerty);
- final String keyboardLayoutSetName = mParams.mKeyboardLayoutSetName;
- final int xmlId = mResources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
+ final int xmlId = getXmlId(mResources, mParams.mKeyboardLayoutSetName);
try {
parseKeyboardLayoutSet(mResources, xmlId);
- } catch (final IOException e) {
- throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
- } catch (final XmlPullParserException e) {
- throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
+ } catch (final IOException | XmlPullParserException e) {
+ throw new RuntimeException(e.getMessage() + " in " + mParams.mKeyboardLayoutSetName,
+ e);
}
return new KeyboardLayoutSet(mContext, mParams);
}
+ private static int getXmlId(final Resources resources, final String keyboardLayoutSetName) {
+ final String packageName = resources.getResourcePackageName(
+ R.xml.keyboard_layout_set_qwerty);
+ return resources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
+ }
+
private void parseKeyboardLayoutSet(final Resources res, final int resId)
throws XmlPullParserException, IOException {
final XmlResourceParser parser = res.getXml(resId);
@@ -407,17 +464,8 @@ public final class KeyboardLayoutSet {
private void parseKeyboardLayoutSetFeature(final XmlPullParser parser)
throws XmlPullParserException, IOException {
- final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.KeyboardLayoutSet_Feature);
- try {
- final int scriptId = a.getInt(
- R.styleable.KeyboardLayoutSet_Feature_supportedScript,
- ScriptUtils.SCRIPT_LATIN);
- XmlParseUtils.checkEndTag(TAG_FEATURE, parser);
- setScriptId(scriptId);
- } finally {
- a.recycle();
- }
+ final int scriptId = readScriptIdFromTagFeature(mResources, parser);
+ setScriptId(scriptId);
}
private static int getKeyboardMode(final EditorInfo editorInfo) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
index 6d8c8b76f..8a9688ac4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java
@@ -22,7 +22,6 @@ import android.os.Build.VERSION_CODES;
import android.preference.PreferenceManager;
import android.util.Log;
-import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.BuildCompatUtils;
import com.android.inputmethod.latin.R;
@@ -45,7 +44,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES;
- @UsedForTesting
+ /* package private for testing */
static final KeyboardTheme[] KEYBOARD_THEMES = {
new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS,
// This has never been selected because we support ICS or later.
@@ -57,6 +56,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
// Default theme for LXX.
BuildCompatUtils.VERSION_CODES_LXX),
new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark,
+ // This has never been selected as default theme.
VERSION_CODES.BASE),
};
@@ -68,7 +68,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
public final int mThemeId;
public final int mStyleId;
public final String mThemeName;
- private final int mMinApiVersion;
+ public final int mMinApiVersion;
// Note: The themeId should be aligned with "themeId" attribute of Keyboard style
// in values/themes-<style>.xml.
@@ -98,7 +98,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
return mThemeId;
}
- @UsedForTesting
+ /* package private for testing */
static KeyboardTheme searchKeyboardThemeById(final int themeId,
final KeyboardTheme[] availableThemeIds) {
// TODO: This search algorithm isn't optimal if there are many themes.
@@ -110,7 +110,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
return null;
}
- @UsedForTesting
+ /* package private for testing */
static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs,
final int sdkVersion, final KeyboardTheme[] availableThemeArray) {
final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null);
@@ -150,7 +150,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
saveKeyboardThemeId(themeId, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT);
}
- @UsedForTesting
+ /* package private for testing */
static String getPreferenceKey(final int sdkVersion) {
if (sdkVersion <= VERSION_CODES.KITKAT) {
return KLP_KEYBOARD_THEME_KEY;
@@ -158,7 +158,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
return LXX_KEYBOARD_THEME_KEY;
}
- @UsedForTesting
+ /* package private for testing */
static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs,
final int sdkVersion) {
final String prefKey = getPreferenceKey(sdkVersion);
@@ -171,6 +171,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray);
}
+ /* package private for testing */
static KeyboardTheme[] getAvailableThemeArray(final Context context) {
if (AVAILABLE_KEYBOARD_THEMES == null) {
final int[] availableThemeIdStringArray = context.getResources().getIntArray(
@@ -184,11 +185,12 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
}
AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray(
new KeyboardTheme[availableThemeList.size()]);
+ Arrays.sort(AVAILABLE_KEYBOARD_THEMES);
}
return AVAILABLE_KEYBOARD_THEMES;
}
- @UsedForTesting
+ /* package private for testing */
static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion,
final KeyboardTheme[] availableThemeArray) {
final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null);
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index e7be6de4c..06f9ced92 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -28,6 +28,7 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Typeface;
import android.preference.PreferenceManager;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -57,8 +58,10 @@ import com.android.inputmethod.latin.RichInputMethodSubtype;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.settings.DebugSettings;
import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.TypefaceUtils;
+import java.util.Locale;
import java.util.WeakHashMap;
/**
@@ -855,8 +858,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private String layoutLanguageOnSpacebar(final Paint paint,
final RichInputMethodSubtype subtype, final int width) {
if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_MULTIPLE) {
- // TODO: return an appropriate string
- return "";
+ final Locale[] locales = subtype.getLocales();
+ final String[] languages = new String[locales.length];
+ for (int i = 0; i < locales.length; ++i) {
+ languages[i] = StringUtils.toUpperCaseOfStringForLocale(
+ locales[i].getLanguage(), true /* needsToUpperCase */, Locale.ROOT);
+ }
+ return TextUtils.join(" / ", languages);
}
// Choose appropriate language name to fit into the width.
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
index c22717f95..2b7d2ce3c 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -30,6 +30,7 @@ import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* A controller class of the add-to-dictionary indicator (a.k.a. TextDecorator). This class
@@ -56,6 +57,7 @@ public class TextDecorator {
private String mWaitingWord = null;
private int mWaitingCursorStart = INVALID_CURSOR_INDEX;
private int mWaitingCursorEnd = INVALID_CURSOR_INDEX;
+ @Nullable
private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null;
@Nonnull
@@ -150,7 +152,7 @@ public class TextDecorator {
* mode.</p>
* @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}.
*/
- public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
+ public void onUpdateCursorAnchorInfo(@Nullable final CursorAnchorInfoCompatWrapper info) {
mCursorAnchorInfoWrapper = info;
// Do not use layoutLater() to minimize the latency.
layoutImmediately();
@@ -182,7 +184,7 @@ public class TextDecorator {
private void layoutMain() {
final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper;
- if (info == null || !info.isAvailable()) {
+ if (info == null) {
cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available.");
return;
}
@@ -301,7 +303,7 @@ public class TextDecorator {
*/
private static final class LayoutInvalidator {
private final HandlerImpl mHandler;
- public LayoutInvalidator(final TextDecorator ownerInstance) {
+ public LayoutInvalidator(@Nonnull final TextDecorator ownerInstance) {
mHandler = new HandlerImpl(ownerInstance);
}
@@ -309,7 +311,7 @@ public class TextDecorator {
private static final class HandlerImpl
extends LeakGuardHandlerWrapper<TextDecorator> {
- public HandlerImpl(final TextDecorator ownerInstance) {
+ public HandlerImpl(@Nonnull final TextDecorator ownerInstance) {
super(ownerInstance);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
index 4f8a105d5..1a55359f5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
@@ -23,6 +23,8 @@ import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
+import javax.annotation.Nonnull;
+
// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so.
public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> {
public interface Callbacks {
@@ -34,7 +36,7 @@ public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> {
private static final int MSG_DISMISS_KEY_PREVIEW = 0;
private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
- public DrawingHandler(final Callbacks ownerInstance) {
+ public DrawingHandler(@Nonnull final Callbacks ownerInstance) {
super(ownerInstance);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
index ec7b9b024..80b299bf5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
@@ -27,6 +27,8 @@ import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
+import javax.annotation.Nonnull;
+
// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so.
public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy {
public interface Callbacks {
@@ -45,7 +47,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple
private final int mIgnoreAltCodeKeyTimeout;
private final int mGestureRecognitionUpdateTime;
- public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout,
+ public TimerHandler(@Nonnull final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout,
final int gestureRecognitionUpdateTime) {
super(ownerInstance);
mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout;
diff --git a/java/src/com/android/inputmethod/latin/BackupAgent.java b/java/src/com/android/inputmethod/latin/BackupAgent.java
index 1f044618a..b2d92b30c 100644
--- a/java/src/com/android/inputmethod/latin/BackupAgent.java
+++ b/java/src/com/android/inputmethod/latin/BackupAgent.java
@@ -17,15 +17,41 @@
package com.android.inputmethod.latin;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupDataInput;
import android.app.backup.SharedPreferencesBackupHelper;
+import android.content.SharedPreferences;
+import android.os.ParcelFileDescriptor;
+
+import com.android.inputmethod.latin.settings.LocalSettingsConstants;
+
+import java.io.IOException;
/**
- * Backs up the Latin IME shared preferences.
+ * Backup/restore agent for LatinIME.
+ * Currently it backs up the default shared preferences.
*/
public final class BackupAgent extends BackupAgentHelper {
+ private static final String PREF_SUFFIX = "_preferences";
+
@Override
public void onCreate() {
addHelper("shared_pref", new SharedPreferencesBackupHelper(this,
- getPackageName() + "_preferences"));
+ getPackageName() + PREF_SUFFIX));
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ // Let the restore operation go through
+ super.onRestore(data, appVersionCode, newState);
+
+ // Remove the preferences that we don't want restored.
+ final SharedPreferences.Editor prefEditor = getSharedPreferences(
+ getPackageName() + PREF_SUFFIX, MODE_PRIVATE).edit();
+ for (final String key : LocalSettingsConstants.PREFS_TO_SKIP_RESTORING) {
+ prefEditor.remove(key);
+ }
+ // Flush the changes to disk.
+ prefEditor.commit();
}
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 10dea749d..2fece7c85 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -70,7 +70,7 @@ public final class BinaryDictionary extends Dictionary {
private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 5;
private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0;
private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1;
- private static final int FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX = 2;
+ private static final int FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX = 2;
private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3;
private static final int FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX = 4;
@@ -179,9 +179,10 @@ public final class BinaryDictionary extends Dictionary {
boolean[] isBeginningOfSentenceArray, int[] word);
private static native void getWordPropertyNative(long dict, int[] word,
boolean isBeginningOfSentence, int[] outCodePoints, boolean[] outFlags,
- int[] outProbabilityInfo, ArrayList<int[]> outBigramTargets,
- ArrayList<int[]> outBigramProbabilityInfo, ArrayList<int[]> outShortcutTargets,
- ArrayList<Integer> outShortcutProbabilities);
+ int[] outProbabilityInfo, ArrayList<int[][]> outNgramPrevWordsArray,
+ ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray,
+ ArrayList<int[]> outNgramTargets, ArrayList<int[]> outNgramProbabilityInfo,
+ ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities);
private static native int getNextWordNative(long dict, int token, int[] outCodePoints,
boolean[] outIsBeginningOfSentence);
private static native void getSuggestionsNative(long dict, long proximityInfo,
@@ -201,8 +202,7 @@ public final class BinaryDictionary extends Dictionary {
int[] word, int probability, int timestamp);
private static native boolean removeNgramEntryNative(long dict,
int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word);
- // TODO: Rename to updateEntriesForWordWithNgramContextNative.
- private static native boolean updateCounterNative(long dict,
+ private static native boolean updateEntriesForWordWithNgramContextNative(long dict,
int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray,
int[] word, boolean isValidWord, int count, int timestamp);
private static native int addMultipleDictionaryEntriesNative(long dict,
@@ -292,6 +292,7 @@ public final class BinaryDictionary extends Dictionary {
settingsValuesForSuggestion.mSpaceAwareGestureEnabled);
session.mNativeSuggestOptions.setAdditionalFeaturesOptions(
settingsValuesForSuggestion.mAdditionalFeaturesSettingValues);
+ session.mNativeSuggestOptions.setWeightForLocale(weightForLocale);
if (inOutWeightOfLangModelVsSpatialModel != null) {
session.mInputOutputWeightOfLangModelVsSpatialModel[0] =
inOutWeightOfLangModelVsSpatialModel[0];
@@ -388,20 +389,25 @@ public final class BinaryDictionary extends Dictionary {
final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT];
final int[] outProbabilityInfo =
new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT];
- final ArrayList<int[]> outBigramTargets = new ArrayList<>();
- final ArrayList<int[]> outBigramProbabilityInfo = new ArrayList<>();
+ final ArrayList<int[][]> outNgramPrevWordsArray = new ArrayList<>();
+ final ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray =
+ new ArrayList<>();
+ final ArrayList<int[]> outNgramTargets = new ArrayList<>();
+ final ArrayList<int[]> outNgramProbabilityInfo = new ArrayList<>();
final ArrayList<int[]> outShortcutTargets = new ArrayList<>();
final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>();
getWordPropertyNative(mNativeDict, codePoints, isBeginningOfSentence, outCodePoints,
- outFlags, outProbabilityInfo, outBigramTargets, outBigramProbabilityInfo,
- outShortcutTargets, outShortcutProbabilities);
+ outFlags, outProbabilityInfo, outNgramPrevWordsArray,
+ outNgramPrevWordIsBeginningOfSentenceArray, outNgramTargets,
+ outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities);
return new WordProperty(codePoints,
outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX],
outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX],
- outFlags[FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX],
+ outFlags[FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX],
outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX],
outFlags[FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX], outProbabilityInfo,
- outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
+ outNgramPrevWordsArray, outNgramPrevWordIsBeginningOfSentenceArray,
+ outNgramTargets, outNgramProbabilityInfo, outShortcutTargets,
outShortcutProbabilities);
}
@@ -506,7 +512,7 @@ public final class BinaryDictionary extends Dictionary {
final boolean[] isBeginningOfSentenceArray = new boolean[ngramContext.getPrevWordCount()];
ngramContext.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
final int[] wordCodePoints = StringUtils.toCodePointArray(word);
- if (!updateCounterNative(mNativeDict, prevWordCodePointArrays,
+ if (!updateEntriesForWordWithNgramContextNative(mNativeDict, prevWordCodePointArrays,
isBeginningOfSentenceArray, wordCodePoints, isValidWord, count, timestamp)) {
return false;
}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 43561ba4b..e66847b56 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -16,6 +16,7 @@
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;
@@ -36,19 +37,19 @@ public abstract class Dictionary {
// The following types do not actually come from real dictionary instances, so we create
// corresponding instances.
public static final String TYPE_USER_TYPED = "user_typed";
- public static final Dictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED);
+ public static final PhonyDictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED);
public static final String TYPE_APPLICATION_DEFINED = "application_defined";
- public static final Dictionary DICTIONARY_APPLICATION_DEFINED =
+ public static final PhonyDictionary DICTIONARY_APPLICATION_DEFINED =
new PhonyDictionary(TYPE_APPLICATION_DEFINED);
public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such
- public static final Dictionary DICTIONARY_HARDCODED =
+ public static final PhonyDictionary DICTIONARY_HARDCODED =
new PhonyDictionary(TYPE_HARDCODED);
// Spawned by resuming suggestions. Comes from a span that was in the TextView.
public static final String TYPE_RESUMED = "resumed";
- public static final Dictionary DICTIONARY_RESUMED =
+ public static final PhonyDictionary DICTIONARY_RESUMED =
new PhonyDictionary(TYPE_RESUMED);
// The following types of dictionary have actual functional instances. We don't need final
@@ -182,9 +183,10 @@ public abstract class Dictionary {
* Not a true dictionary. A placeholder used to indicate suggestions that don't come from any
* real dictionary.
*/
- private static class PhonyDictionary extends Dictionary {
- // This class is not publicly instantiable.
- private PhonyDictionary(final String type) {
+ @UsedForTesting
+ static class PhonyDictionary extends Dictionary {
+ @UsedForTesting
+ PhonyDictionary(final String type) {
super(type, null);
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 6a63bfda7..08035dfd6 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -257,6 +257,12 @@ public class DictionaryFacilitator {
}
public void switchMostProbableLanguage(final Locale locale) {
+ if (null == locale) {
+ // In many cases, there is no locale to a committed word. For example, a typed word
+ // that does not auto-correct has no locale. In this case we simply do not change
+ // the most probable language.
+ return;
+ }
final DictionaryGroup newMostProbableDictionaryGroup =
findDictionaryGroupWithLocale(mDictionaryGroups, locale);
mMostProbableDictionaryGroup.mWeightForTypingInLocale =
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index d24f80a45..d58373ff6 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -64,9 +64,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
- private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000;
- private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000;
-
/**
* The maximum length of a word in this dictionary.
*/
@@ -225,10 +222,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
- attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY,
- String.valueOf(DEFAULT_MAX_UNIGRAM_COUNT));
- attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY,
- String.valueOf(DEFAULT_MAX_BIGRAM_COUNT));
return attributeMap;
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ec45462c4..91a4ec84c 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -60,6 +60,8 @@ import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
+import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils;
+import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater;
import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
import com.android.inputmethod.event.Event;
import com.android.inputmethod.event.HardwareEventDecoder;
@@ -85,7 +87,6 @@ import com.android.inputmethod.latin.settings.SettingsActivity;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
-import com.android.inputmethod.latin.sync.BeanstalkManager;
import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
@@ -109,6 +110,8 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+
/**
* Input method implementation for Qwerty'ish keyboard.
*/
@@ -154,6 +157,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
private View mInputView;
+ private InsetsUpdater mInsetsUpdater;
private SuggestionStripView mSuggestionStripView;
private TextView mExtractEditText;
@@ -206,7 +210,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private int mDelayInMillisecondsToUpdateSuggestions;
private int mDelayInMillisecondsToUpdateShiftState;
- public UIHandler(final LatinIME ownerInstance) {
+ public UIHandler(@Nonnull final LatinIME ownerInstance) {
super(ownerInstance);
}
@@ -558,7 +562,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
AudioAndHapticFeedbackManager.init(this);
AccessibilityUtils.init(this);
mStatsUtilsManager.onCreate(this /* context */);
- BeanstalkManager.getInstance(this /* context */).onCreate();
super.onCreate();
mHandler.onCreate();
@@ -567,6 +570,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: Resolve mutual dependencies of {@link #loadSettings()} and
// {@link #resetDictionaryFacilitatorIfNecessary()}.
loadSettings();
+ mSubtypeSwitcher.onSubtypeChanged(mRichImm.getCurrentRawSubtype());
resetDictionaryFacilitatorIfNecessary();
// Register to receive ringer mode change and network state change.
@@ -706,7 +710,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
unregisterReceiver(mDictionaryPackInstallReceiver);
unregisterReceiver(mDictionaryDumpBroadcastReceiver);
mStatsUtilsManager.onDestroy();
- BeanstalkManager.getInstance(this /* context */).onDestroy();
super.onDestroy();
}
@@ -754,6 +757,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void setInputView(final View view) {
super.setInputView(view);
mInputView = view;
+ mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view);
updateSoftInputWindowLayoutParameters();
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
if (hasSuggestionStripView()) {
@@ -791,23 +795,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
- onExtractTextViewPreDraw();
+ // CursorAnchorInfo is used on L and later.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (isFullscreenMode() && mExtractEditText != null) {
+ mInputLogic.onUpdateCursorAnchorInfo(
+ CursorAnchorInfoUtils.extractFromTextView(mExtractEditText));
+ }
+ }
return true;
}
};
- private void onExtractTextViewPreDraw() {
- // CursorAnchorInfo is available on L and later.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.L) {
- return;
- }
- if (!isFullscreenMode() || mExtractEditText == null) {
- return;
- }
- final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText);
- mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
- }
-
@Override
public void setCandidatesView(final View view) {
// To ensure that CandidatesView will never be set.
@@ -842,8 +840,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
- final RichInputMethodSubtype richSubtype = new RichInputMethodSubtype(subtype);
- mSubtypeSwitcher.onSubtypeChanged(richSubtype);
+ mSubtypeSwitcher.onSubtypeChanged(subtype);
mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype),
mSettings.getCurrent());
loadKeyboard();
@@ -860,6 +857,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// also wouldn't be consuming gesture data.
mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
mRichImm.clearSubtypeCaches();
+ mSubtypeSwitcher.refreshSubtypeInfo();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
switcher.updateKeyboardTheme();
final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
@@ -1078,7 +1076,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// with cursor movement when we have a hardware keyboard since we are not in charge.
final SettingsValues settingsValues = mSettings.getCurrent();
if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED)
- && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) {
+ && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
+ settingsValues)) {
mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
getCurrentRecapitalizeState());
}
@@ -1090,7 +1089,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (isFullscreenMode()) {
return;
}
- mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
+ mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.wrap(info));
}
/**
@@ -1191,6 +1190,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// no visual element will be shown on the screen.
outInsets.touchableInsets = inputHeight;
outInsets.visibleTopInsets = inputHeight;
+ mInsetsUpdater.setInsets(outInsets);
return;
}
final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes()
@@ -1211,6 +1211,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
outInsets.contentTopInsets = visibleTopY;
outInsets.visibleTopInsets = visibleTopY;
+ mInsetsUpdater.setInsets(outInsets);
}
public void startShowingInputView(final boolean needsToLoadKeyboard) {
@@ -1627,7 +1628,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
- public void showAddToDictionaryHint(final String word) {
+ public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip) {
if (!hasSuggestionStripView()) {
return;
}
@@ -1637,7 +1638,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
wordToShow = word;
}
- mSuggestionStripView.showAddToDictionaryHint(wordToShow);
+ mSuggestionStripView.showAddToDictionaryHint(wordToShow,
+ isFromSuggestionStrip /* shouldShowWordToSave */);
}
// This will show either an empty suggestion strip (if prediction is enabled) or
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 62a258b20..a3f7bb4d6 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -860,9 +860,10 @@ public final class RichInputConnection implements PrivateCommandPerformer {
* than it really is.
*/
public void tryFixLyingCursorPosition() {
+ mIC = mParent.getCurrentInputConnection();
final CharSequence textBeforeCursor = getTextBeforeCursor(
Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
- final CharSequence selectedText = mIC.getSelectedText(0 /* flags */);
+ final CharSequence selectedText = null == mIC ? null : mIC.getSelectedText(0 /* flags */);
if (null == textBeforeCursor ||
(!TextUtils.isEmpty(selectedText) && mExpectedSelEnd == mExpectedSelStart)) {
// If textBeforeCursor is null, we have no idea what kind of text field we have or if
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
index 0d5ce7d6d..e6df35bea 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Resources;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
@@ -29,6 +30,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
@@ -50,6 +52,7 @@ public class RichInputMethodManager {
private static final RichInputMethodManager sInstance = new RichInputMethodManager();
+ private Context mContext;
private InputMethodManagerCompatWrapper mImmWrapper;
private InputMethodInfoCache mInputMethodInfoCache;
final HashMap<InputMethodInfo, List<InputMethodSubtype>>
@@ -83,6 +86,7 @@ public class RichInputMethodManager {
return;
}
mImmWrapper = new InputMethodManagerCompatWrapper(context);
+ mContext = context;
mInputMethodInfoCache = new InputMethodInfoCache(
mImmWrapper.mImm, context.getPackageName());
@@ -298,14 +302,14 @@ public class RichInputMethodManager {
return INDEX_NOT_FOUND;
}
- public RichInputMethodSubtype getCurrentInputMethodSubtype(
- final RichInputMethodSubtype defaultSubtype) {
- final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype();
- if (currentSubtype == null) {
- return defaultSubtype;
- }
- // TODO: Determine locales to use for multi-lingual use.
- return new RichInputMethodSubtype(currentSubtype);
+ public InputMethodSubtype getCurrentRawSubtype() {
+ return mImmWrapper.mImm.getCurrentInputMethodSubtype();
+ }
+
+ public RichInputMethodSubtype createCurrentRichInputMethodSubtype(
+ final InputMethodSubtype rawSubtype) {
+ return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype,
+ mContext);
}
public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index c339e96fb..6fc549549 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -36,7 +36,6 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper;
import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.HashSet;
@@ -58,6 +57,7 @@ public final class SubtypeSwitcher {
new LanguageOnSpacebarHelper();
private InputMethodInfo mShortcutInputMethodInfo;
private InputMethodSubtype mShortcutSubtype;
+ private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
private RichInputMethodSubtype mNoLanguageSubtype;
private RichInputMethodSubtype mEmojiSubtype;
private boolean mIsNetworkConnected;
@@ -117,10 +117,14 @@ public final class SubtypeSwitcher {
final NetworkInfo info = connectivityManager.getActiveNetworkInfo();
mIsNetworkConnected = (info != null && info.isConnected());
- onSubtypeChanged(getCurrentSubtype());
+ refreshSubtypeInfo();
updateParametersOnStartInputView();
}
+ public void refreshSubtypeInfo() {
+ onSubtypeChanged(mRichImm.getCurrentRawSubtype());
+ }
+
/**
* Update parameters which are changed outside LatinIME. This parameters affect UI so that they
* should be updated every time onStartInputView is called.
@@ -165,12 +169,14 @@ public final class SubtypeSwitcher {
}
// Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
- public void onSubtypeChanged(final RichInputMethodSubtype newSubtype) {
+ public void onSubtypeChanged(final InputMethodSubtype newSubtype) {
+ final RichInputMethodSubtype richSubtype =
+ mRichImm.createCurrentRichInputMethodSubtype(newSubtype);
if (DBG) {
- Log.w(TAG, "onSubtypeChanged: " + newSubtype.getNameForLogging());
+ Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging());
}
-
- final Locale[] newLocales = newSubtype.getLocales();
+ mCurrentRichInputMethodSubtype = richSubtype;
+ final Locale[] newLocales = richSubtype.getLocales();
if (newLocales.length > 1) {
// In multi-locales mode, the system language is never the same as the input language
// because there is no single input language.
@@ -181,7 +187,7 @@ public final class SubtypeSwitcher {
final boolean sameLocale = systemLocale.equals(newLocale);
final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
final boolean implicitlyEnabled = mRichImm
- .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype.getRawSubtype());
+ .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(
sameLocale || (sameLanguage && implicitlyEnabled));
}
@@ -301,7 +307,7 @@ public final class SubtypeSwitcher {
if (null != sForcedSubtypeForTesting) {
return sForcedSubtypeForTesting;
}
- return mRichImm.getCurrentInputMethodSubtype(getNoLanguageSubtype());
+ return mCurrentRichInputMethodSubtype;
}
public RichInputMethodSubtype getNoLanguageSubtype() {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index e181237a6..e6c138407 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -28,6 +28,7 @@ import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.SuggestionResults;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Locale;
/**
@@ -49,6 +50,16 @@ public final class Suggest {
private static final boolean DBG = DebugFlags.DEBUG_ENABLED;
private final DictionaryFacilitator mDictionaryFacilitator;
+ private static final int MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN = 12;
+ private static final HashMap<String, Integer> sLanguageToMaximumAutoCorrectionWithSpaceLength =
+ new HashMap<>();
+ static {
+ // TODO: should we add Finnish here?
+ // TODO: This should not be hardcoded here but be written in the dictionary header
+ sLanguageToMaximumAutoCorrectionWithSpaceLength.put(Locale.GERMAN.getLanguage(),
+ MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN);
+ }
+
private float mAutoCorrectionThreshold;
public Suggest(final DictionaryFacilitator dictionaryFacilitator) {
@@ -168,8 +179,18 @@ public final class Suggest {
// TODO: we may want to have shortcut-only entries auto-correct in the future.
hasAutoCorrection = false;
} else {
- hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
- suggestionResults.first(), consideredWord, mAutoCorrectionThreshold);
+ final SuggestedWordInfo firstSuggestion = suggestionResults.first();
+ if (!AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
+ firstSuggestion, consideredWord, mAutoCorrectionThreshold)) {
+ // Score is too low for autocorrect
+ hasAutoCorrection = false;
+ } else {
+ // We have a high score, so we need to check if this suggestion is in the correct
+ // form to allow auto-correcting to it in this language. For details of how this
+ // is determined, see #isAllowedByAutoCorrectionWithSpaceFilter.
+ // TODO: this should not have its own logic here but be handled by the dictionary.
+ hasAutoCorrection = isAllowedByAutoCorrectionWithSpaceFilter(firstSuggestion);
+ }
}
if (!TextUtils.isEmpty(typedWord)) {
@@ -287,6 +308,41 @@ public final class Suggest {
return suggestionsList;
}
+ /**
+ * Computes whether this suggestion should be blocked or not in this language
+ *
+ * This function implements a filter that avoids auto-correcting to suggestions that contain
+ * spaces that are above a certain language-dependent character limit. In languages like German
+ * where it's possible to concatenate many words, it often happens our dictionary does not
+ * have the longer words. In this case, we offer a lot of unhelpful suggestions that contain
+ * one or several spaces. Ideally we should understand what the user wants and display useful
+ * suggestions by improving the dictionary and possibly having some specific logic. Until
+ * that's possible we should avoid displaying unhelpful suggestions. But it's hard to tell
+ * whether a suggestion is useful or not. So at least for the time being we block
+ * auto-correction when the suggestion is long and contains a space, which should avoid the
+ * worst damage.
+ * This function is implementing that filter. If the language enforces no such limit, then it
+ * always returns true. If the suggestion contains no space, it also returns true. Otherwise,
+ * it checks the length against the language-specific limit.
+ *
+ * @param info the suggestion info
+ * @return whether it's fine to auto-correct to this.
+ */
+ private boolean isAllowedByAutoCorrectionWithSpaceFilter(final SuggestedWordInfo info) {
+ final Locale locale = info.mSourceDict.mLocale;
+ if (null == locale) {
+ return true;
+ }
+ final Integer maximumLengthForThisLanguage =
+ sLanguageToMaximumAutoCorrectionWithSpaceLength.get(locale.getLanguage());
+ if (null == maximumLengthForThisLanguage) {
+ // This language does not enforce a maximum length to auto-correction
+ return true;
+ }
+ return info.mWord.length() <= maximumLengthForThisLanguage
+ || -1 == info.mWord.indexOf(Constants.CODE_SPACE);
+ }
+
/* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 466576465..e5bf25d5f 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -243,7 +243,8 @@ public class SuggestedWords {
return candidate.isEligibleForAutoCommit() ? candidate : null;
}
- public static final class SuggestedWordInfo {
+ // non-final for testability.
+ public static class SuggestedWordInfo {
public static final int NOT_AN_INDEX = -1;
public static final int NOT_A_CONFIDENCE = -1;
public static final int MAX_SCORE = Integer.MAX_VALUE;
@@ -413,28 +414,6 @@ public class SuggestedWords {
return isPrediction(mInputStyle);
}
- // SuggestedWords is an immutable object, as much as possible. We must not just remove
- // words from the member ArrayList as some other parties may expect the object to never change.
- // This is only ever called by recorrection at the moment, hence the ForRecorrection moniker.
- public SuggestedWords getSuggestedWordsExcludingTypedWordForRecorrection() {
- final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>();
- String typedWord = null;
- for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
- final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
- if (!info.isKindOf(SuggestedWordInfo.KIND_TYPED)) {
- newSuggestions.add(info);
- } else {
- assert(null == typedWord);
- typedWord = info.mWord;
- }
- }
- // We should never autocorrect, so we say the typed word is valid. Also, in this case,
- // no auto-correction should take place hence willAutoCorrect = false.
- return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord,
- true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions,
- SuggestedWords.INPUT_STYLE_RECORRECTION, NOT_A_SEQUENCE_NUMBER);
- }
-
// Creates a new SuggestedWordInfo from the currently suggested words that removes all but the
// last word of all suggestions, separated by a space. This is necessary because when we commit
// a multiple-word suggestion, the IME only retains the last word as the composing word, and
diff --git a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java
index 9445ce4c3..00bcecf52 100644
--- a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java
+++ b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java
@@ -26,7 +26,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.latin.settings.LocalSettingsConstants;
/**
* {@link BroadcastReceiver} for {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION}.
@@ -41,25 +41,14 @@ public class AccountsChangedReceiver extends BroadcastReceiver {
return;
}
+ // Ideally the account preference could live in a different preferences file
+ // that wasn't being backed up and restored, however the preference fragments
+ // currently only deal with the default shared preferences which is why
+ // separating this out into a different file is not trivial currently.
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- final String currentAccount = prefs.getString(Settings.PREF_ACCOUNT_NAME, null);
- if (currentAccount != null) {
- final String[] accounts = getAccountsForLogin(context);
- boolean accountFound = false;
- for (String account : accounts) {
- if (TextUtils.equals(currentAccount, account)) {
- accountFound = true;
- break;
- }
- }
- // The current account was not found in the list of accounts, remove it.
- if (!accountFound) {
- Log.i(TAG, "The current account was removed from the system: " + currentAccount);
- prefs.edit()
- .remove(Settings.PREF_ACCOUNT_NAME)
- .apply();
- }
- }
+ final String currentAccount = prefs.getString(
+ LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
+ removeUnknownAccountFromPreference(prefs, getAccountsForLogin(context), currentAccount);
}
/**
@@ -69,4 +58,24 @@ public class AccountsChangedReceiver extends BroadcastReceiver {
protected String[] getAccountsForLogin(Context context) {
return LoginAccountUtils.getAccountsForLogin(context);
}
+
+ /**
+ * Removes the currentAccount from preferences if it's not found
+ * in the list of current accounts.
+ */
+ private static void removeUnknownAccountFromPreference(final SharedPreferences prefs,
+ final String[] accounts, final String currentAccount) {
+ if (currentAccount == null) {
+ return;
+ }
+ for (final String account : accounts) {
+ if (TextUtils.equals(currentAccount, account)) {
+ return;
+ }
+ }
+ Log.i(TAG, "The current account was removed from the system: " + currentAccount);
+ prefs.edit()
+ .remove(LocalSettingsConstants.PREF_ACCOUNT_NAME)
+ .apply();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 07f2ed3db..5cc61db79 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -305,6 +305,7 @@ public final class InputLogic {
currentKeyboardScriptId, handler);
}
+ mDictionaryFacilitator.switchMostProbableLanguage(suggestionInfo.mSourceDict.mLocale);
final Event event = Event.createSuggestionPickedEvent(suggestionInfo);
final InputTransaction inputTransaction = new InputTransaction(settingsValues,
event, SystemClock.uptimeMillis(), mSpaceState, keyboardShiftState);
@@ -348,7 +349,8 @@ public final class InputLogic {
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
if (shouldShowAddToDictionaryHint) {
- mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
+ mSuggestionStripViewAccessor.suggestAddingToDictionary(suggestion,
+ true /* isFromSuggestionStrip */);
} else {
// If we're not showing the "Touch again to save", then update the suggestion strip.
// That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
@@ -369,10 +371,11 @@ public final class InputLogic {
* @param oldSelEnd old selection end
* @param newSelStart new selection start
* @param newSelEnd new selection end
+ * @param settingsValues the current values of the settings.
* @return whether the cursor has moved as a result of user interaction.
*/
public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd,
- final int newSelStart, final int newSelEnd) {
+ final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) {
if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) {
return false;
}
@@ -397,8 +400,9 @@ public final class InputLogic {
// should be true, but that is if the framework had taken that wrong cursor position
// into account, which means we have to reset the entire composing state whenever there
// is or was a selection regardless of whether it changed or not.
- if (hasOrHadSelection || (selectionChangedOrSafeToReset
- && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
+ if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions()
+ || (selectionChangedOrSafeToReset
+ && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
// If we are composing a word and moving the cursor, we would want to set a
// suggestion span for recorrection to work correctly. Unfortunately, that
// would involve the keyboard committing some new text, which would move the
@@ -1484,6 +1488,11 @@ public final class InputLogic {
if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
final String typedWord = range.mWord.toString();
+ suggestions.add(new SuggestedWordInfo(typedWord,
+ SuggestedWords.MAX_SUGGESTIONS + 1,
+ SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
+ SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+ SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
if (!isResumableWord(settingsValues, typedWord)) {
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
return;
@@ -1516,30 +1525,14 @@ public final class InputLogic {
mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
- if (suggestions.size() <= 0) {
+ if (suggestions.size() <= 1) {
// If there weren't any suggestion spans on this word, suggestions#size() will be 1
// if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
// have no useful suggestions, so we will try to compute some for it instead.
mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING,
SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override
- public void onGetSuggestedWords(
- final SuggestedWords suggestedWordsIncludingTypedWord) {
- final SuggestedWords suggestedWords;
- if (suggestedWordsIncludingTypedWord.size() > 1) {
- // We were able to compute new suggestions for this word.
- // Remove the typed word, since we don't want to display it in this
- // case. The #getSuggestedWordsExcludingTypedWordForRecorrection()
- // method sets willAutoCorrect to false.
- suggestedWords = suggestedWordsIncludingTypedWord
- .getSuggestedWordsExcludingTypedWordForRecorrection();
- } else {
- // No saved suggestions, and we were unable to compute any good one
- // either. Rather than displaying an empty suggestion strip, we'll
- // display the original word alone in the middle.
- // Since there is only one word, willAutoCorrect is false.
- suggestedWords = suggestedWordsIncludingTypedWord;
- }
+ public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
mIsAutoCorrectionIndicatorOn = false;
mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
}});
@@ -1683,7 +1676,8 @@ public final class InputLogic {
mConnection.getExpectedSelectionStart(),
mConnection.getExpectedSelectionEnd());
}
- mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString);
+ mSuggestionStripViewAccessor.suggestAddingToDictionary(originallyTypedWordString,
+ false /* isFromSuggestionStrip */);
} else {
// We have a separator between the word and the cursor: we should show predictions.
inputTransaction.setRequiresUpdateSuggestions();
@@ -2100,6 +2094,10 @@ public final class InputLogic {
final boolean isBatchMode = mWordComposer.isBatchMode();
commitChosenWord(settingsValues, stringToCommit,
LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator);
+ if (null != autoCorrectionOrNull) {
+ mDictionaryFacilitator.switchMostProbableLanguage(
+ autoCorrectionOrNull.mSourceDict.mLocale);
+ }
if (!typedWord.equals(stringToCommit)) {
// This will make the correction flash for a short while as a visual clue
// to the user that auto-correction happened. It has no other effect; in particular
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
index fa7c2c417..644818ba6 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
@@ -40,8 +40,9 @@ public final class DictionaryHeader {
public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
public static final String FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY =
"FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID";
- public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT";
- public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT";
+ public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_ENTRY_COUNT";
+ public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_ENTRY_COUNT";
+ public static final String MAX_TRIGRAM_COUNT_KEY = "MAX_TRIGRAM_ENTRY_COUNT";
public static final String ATTRIBUTE_VALUE_TRUE = "1";
public static final String CODE_POINT_TABLE_KEY = "codePointTable";
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
index a180d060e..1e6cadf03 100644
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
@@ -26,6 +26,8 @@ import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
+import javax.annotation.Nullable;
+
/**
* Utility class for a word with a probability.
*
@@ -49,7 +51,7 @@ public final class WordProperty implements Comparable<WordProperty> {
@UsedForTesting
public WordProperty(final String word, final ProbabilityInfo probabilityInfo,
final ArrayList<WeightedString> shortcutTargets,
- final ArrayList<WeightedString> bigrams,
+ @Nullable final ArrayList<WeightedString> bigrams,
final boolean isNotAWord, final boolean isBlacklistEntry) {
mWord = word;
mProbabilityInfo = probabilityInfo;
@@ -85,7 +87,9 @@ public final class WordProperty implements Comparable<WordProperty> {
public WordProperty(final int[] codePoints, final boolean isNotAWord,
final boolean isBlacklisted, final boolean hasBigram, final boolean hasShortcuts,
final boolean isBeginningOfSentence, final int[] probabilityInfo,
- final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo,
+ final ArrayList<int[][]> ngramPrevWordsArray,
+ final ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray,
+ final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo,
final ArrayList<int[]> shortcutTargets,
final ArrayList<Integer> shortcutProbabilities) {
mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
@@ -98,15 +102,15 @@ public final class WordProperty implements Comparable<WordProperty> {
mHasShortcuts = hasShortcuts;
mHasNgrams = hasBigram;
- final int relatedNgramCount = bigramTargets.size();
+ final int relatedNgramCount = ngramTargets.size();
final WordInfo currentWordInfo =
mIsBeginningOfSentence ? WordInfo.BEGINNING_OF_SENTENCE : new WordInfo(mWord);
final NgramContext ngramContext = new NgramContext(currentWordInfo);
for (int i = 0; i < relatedNgramCount; i++) {
final String ngramTargetString =
- StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i));
+ StringUtils.getStringFromNullTerminatedCodePointArray(ngramTargets.get(i));
final WeightedString ngramTarget = new WeightedString(ngramTargetString,
- createProbabilityInfoFromArray(bigramProbabilityInfo.get(i)));
+ createProbabilityInfoFromArray(ngramProbabilityInfo.get(i)));
// TODO: Support n-gram.
ngrams.add(new NgramProperty(ngramTarget, ngramContext));
}
@@ -180,7 +184,8 @@ public final class WordProperty implements Comparable<WordProperty> {
&& mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams;
}
- private <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) {
+ // TDOO: Have a utility method like java.util.Objects.equals.
+ private static <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) {
if (null == a) {
return null == b;
}
diff --git a/java/src/com/android/inputmethod/latin/network/AuthException.java b/java/src/com/android/inputmethod/latin/network/AuthException.java
new file mode 100644
index 000000000..1bce4c156
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/network/AuthException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.network;
+
+/**
+ * Authentication exception. When this exception is thrown, the client may
+ * try to refresh the authentication token and try again.
+ */
+public class AuthException extends Exception {
+ public AuthException() {
+ super();
+ }
+
+ public AuthException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public AuthException(String detailMessage) {
+ super(detailMessage);
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java
index 0d0cbe169..e2d24fd0a 100644
--- a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java
+++ b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java
@@ -16,7 +16,7 @@
package com.android.inputmethod.latin.network;
-import com.android.inputmethod.annotations.UsedForTesting;
+import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.IOException;
@@ -30,25 +30,17 @@ import javax.annotation.Nullable;
/**
* A client for executing HTTP requests synchronously.
* This must never be called from the main thread.
- *
- * TODO: Remove @UsedForTesting after this is actually used.
*/
-@UsedForTesting
public class BlockingHttpClient {
+ private static final boolean DEBUG = false;
+ private static final String TAG = BlockingHttpClient.class.getSimpleName();
+
private final HttpURLConnection mConnection;
/**
* Interface that handles processing the response for a request.
*/
- public interface ResponseProcessor {
- /**
- * Called when the HTTP request fails with an error.
- *
- * @param httpStatusCode The status code of the HTTP response.
- * @param message The HTTP response message, if any, or null.
- */
- void onError(int httpStatusCode, @Nullable String message);
-
+ public interface ResponseProcessor<T> {
/**
* Called when the HTTP request finishes successfully.
* The {@link InputStream} is closed by the client after the method finishes,
@@ -56,13 +48,9 @@ public class BlockingHttpClient {
*
* @param response An input stream that can be used to read the HTTP response.
*/
- void onSuccess(InputStream response);
+ T onSuccess(InputStream response) throws IOException;
}
- /**
- * TODO: Remove @UsedForTesting after this is actually used.
- */
- @UsedForTesting
public BlockingHttpClient(HttpURLConnection connection) {
mConnection = connection;
}
@@ -70,16 +58,19 @@ public class BlockingHttpClient {
/**
* Executes the request on the underlying {@link HttpURLConnection}.
*
- * TODO: Remove @UsedForTesting after this is actually used.
- *
* @param request The request payload, if any, or null.
- * @param responeProcessor A processor for the HTTP response.
+ * @param responseProcessor A processor for the HTTP response.
*/
- @UsedForTesting
- public void execute(@Nullable byte[] request, @Nonnull ResponseProcessor responseProcessor)
- throws IOException {
+ public <T> T execute(@Nullable byte[] request, @Nonnull ResponseProcessor<T> responseProcessor)
+ throws IOException, AuthException, HttpException {
+ if (DEBUG) {
+ Log.d(TAG, "execute: " + mConnection.getURL());
+ }
try {
if (request != null) {
+ if (DEBUG) {
+ Log.d(TAG, "request size: " + request.length);
+ }
OutputStream out = new BufferedOutputStream(mConnection.getOutputStream());
out.write(request);
out.flush();
@@ -88,9 +79,17 @@ public class BlockingHttpClient {
final int responseCode = mConnection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
- responseProcessor.onError(responseCode, mConnection.getResponseMessage());
+ Log.w(TAG, "Response error: " + responseCode + ", Message: "
+ + mConnection.getResponseMessage());
+ if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ throw new AuthException(mConnection.getResponseMessage());
+ }
+ throw new HttpException(responseCode);
} else {
- responseProcessor.onSuccess(mConnection.getInputStream());
+ if (DEBUG) {
+ Log.d(TAG, "request executed successfully");
+ }
+ return responseProcessor.onSuccess(mConnection.getInputStream());
}
} finally {
mConnection.disconnect();
diff --git a/java/src/com/android/inputmethod/latin/network/HttpException.java b/java/src/com/android/inputmethod/latin/network/HttpException.java
new file mode 100644
index 000000000..b9d8b6372
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/network/HttpException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.network;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+/**
+ * The HttpException exception represents a XML/HTTP fault with a HTTP status code.
+ */
+public class HttpException extends Exception {
+
+ /**
+ * The HTTP status code.
+ */
+ private final int mStatusCode;
+
+ /**
+ * @param statusCode int HTTP status code.
+ */
+ public HttpException(int statusCode) {
+ super("Response Code: " + statusCode);
+ mStatusCode = statusCode;
+ }
+
+ /**
+ * @return the HTTP status code related to this exception.
+ */
+ @UsedForTesting
+ public int getHttpStatusCode() {
+ return mStatusCode;
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java
index 35b65be56..502f72f17 100644
--- a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java
+++ b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java
@@ -37,6 +37,11 @@ public class HttpUrlConnectionBuilder {
private static final int DEFAULT_TIMEOUT_MILLIS = 5 * 1000;
/**
+ * Request header key for authentication.
+ */
+ public static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
+
+ /**
* Request header key for cache control.
*/
public static final String KEY_CACHE_CONTROL = "Cache-Control";
@@ -78,7 +83,7 @@ public class HttpUrlConnectionBuilder {
* Sets the URL that'll be used for the request.
* This *must* be set before calling {@link #build()}
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpUrlConnectionBuilder setUrl(String url) throws MalformedURLException {
@@ -92,7 +97,7 @@ public class HttpUrlConnectionBuilder {
/**
* Sets the connect timeout. Defaults to {@value #DEFAULT_TIMEOUT} milliseconds.
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpUrlConnectionBuilder setConnectTimeout(int timeoutMillis) {
@@ -107,7 +112,7 @@ public class HttpUrlConnectionBuilder {
/**
* Sets the read timeout. Defaults to {@value #DEFAULT_TIMEOUT} milliseconds.
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpUrlConnectionBuilder setReadTimeout(int timeoutMillis) {
@@ -122,7 +127,7 @@ public class HttpUrlConnectionBuilder {
/**
* Adds an entry to the request header.
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpUrlConnectionBuilder addHeader(String key, String value) {
@@ -131,10 +136,21 @@ public class HttpUrlConnectionBuilder {
}
/**
+ * Sets an authentication token.
+ *
+ * TODO: Remove @UsedForTesting after this method is actually used.
+ */
+ @UsedForTesting
+ public HttpUrlConnectionBuilder setAuthToken(String value) {
+ mHeaderMap.put(HTTP_HEADER_AUTHORIZATION, value);
+ return this;
+ }
+
+ /**
* Sets the request to be executed such that the input is not buffered.
* This may be set when the request size is known beforehand.
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpUrlConnectionBuilder setFixedLengthForStreaming(int length) {
@@ -145,7 +161,7 @@ public class HttpUrlConnectionBuilder {
/**
* Indicates if the request can use cached responses or not.
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpUrlConnectionBuilder setUseCache(boolean useCache) {
@@ -161,7 +177,7 @@ public class HttpUrlConnectionBuilder {
* @see #MODE_DOWNLOAD_ONLY
* @see #MODE_BI_DIRECTIONAL
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used
*/
@UsedForTesting
public HttpUrlConnectionBuilder setMode(int mode) {
@@ -177,7 +193,7 @@ public class HttpUrlConnectionBuilder {
/**
* Builds the {@link HttpURLConnection} instance that can be used to execute the request.
*
- * TODO: Remove @UsedForTesting after this is actually used.
+ * TODO: Remove @UsedForTesting after this method is actually used.
*/
@UsedForTesting
public HttpURLConnection build() throws IOException {
@@ -210,4 +226,4 @@ public class HttpUrlConnectionBuilder {
}
return connection;
}
-}
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
index 32880038f..4bd15d037 100644
--- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
@@ -16,6 +16,9 @@
package com.android.inputmethod.latin.settings;
+import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ACCOUNT_NAME;
+import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ENABLE_CLOUD_SYNC;
+
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -32,8 +35,8 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.accounts.LoginAccountUtils;
+import com.android.inputmethod.latin.accounts.AccountStateChangedListener;
import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.sync.BeanstalkManager;
import javax.annotation.Nullable;
@@ -41,19 +44,17 @@ import javax.annotation.Nullable;
* "Accounts & Privacy" settings sub screen.
*
* This settings sub screen handles the following preferences:
- * <li> Account selection/management for IME
- * <li> TODO: Sync preferences
- * <li> TODO: Privacy preferences
- * <li> Sync now
+ * <li> Account selection/management for IME </li>
+ * <li> Sync preferences </li>
+ * <li> Privacy preferences </li>
*/
public final class AccountsSettingsFragment extends SubScreenFragment {
+ private static final String PREF_SYNC_NOW = "pref_beanstalk";
+
static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
- static final String PREF_SYNC_NOW = "pref_beanstalk";
- private final DialogInterface.OnClickListener mAccountSelectedListener =
- new AccountSelectedListener();
- private final DialogInterface.OnClickListener mAccountSignedOutListener =
- new AccountSignedOutListener();
+ private final DialogInterface.OnClickListener mAccountChangedListener =
+ new AccountChangedListener();
private final Preference.OnPreferenceClickListener mSyncNowListener = new SyncNowListener();
@Override
@@ -81,47 +82,56 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
removePreference(Settings.PREF_ENABLE_METRICS_LOGGING);
}
+ if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
+ removePreference(PREF_ACCCOUNT_SWITCHER);
+ removePreference(PREF_ENABLE_CLOUD_SYNC);
+ removePreference(PREF_SYNC_NOW);
+ }
if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
+ removePreference(PREF_ENABLE_CLOUD_SYNC);
removePreference(PREF_SYNC_NOW);
} else {
final Preference syncNowPreference = findPreference(PREF_SYNC_NOW);
- if (syncNowPreference != null) {
- syncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
- }
+ syncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
}
}
@Override
public void onResume() {
super.onResume();
- refreshUi();
+ refreshAccountAndDependentPreferences(getSignedInAccountName());
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
- // TODO: Look at the preference that changed before refreshing the view.
- refreshUi();
- }
-
- private void refreshUi() {
- refreshAccountSelection();
- refreshSyncNow();
+ if (TextUtils.equals(key, PREF_ACCOUNT_NAME)) {
+ refreshAccountAndDependentPreferences(
+ prefs.getString(PREF_ACCOUNT_NAME, null));
+ } else if (TextUtils.equals(key, PREF_ENABLE_CLOUD_SYNC)) {
+ final boolean syncEnabled = prefs.getBoolean(PREF_ENABLE_CLOUD_SYNC, false);
+ AccountStateChangedListener.onSyncPreferenceChanged(
+ getSignedInAccountName(), syncEnabled);
+ }
}
- private void refreshAccountSelection() {
+ private void refreshAccountAndDependentPreferences(@Nullable final String currentAccount) {
if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
return;
}
- final String currentAccount = getCurrentlySelectedAccount();
final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
if (currentAccount == null) {
// No account is currently selected.
accountSwitcher.setSummary(getString(R.string.no_accounts_selected));
+ // Disable the sync preference UI.
+ disableSyncPreference();
} else {
// Set the currently selected account.
accountSwitcher.setSummary(getString(R.string.account_selected, currentAccount));
+ // Enable the sync preference UI.
+ enableSyncPreference();
}
+ // Set up onClick listener for the account picker preference.
final Context context = getActivity();
final String[] accountsForLogin = LoginAccountUtils.getAccountsForLogin(context);
accountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -129,45 +139,50 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
public boolean onPreferenceClick(Preference preference) {
if (accountsForLogin.length == 0) {
// TODO: Handle account addition.
- Toast.makeText(getActivity(),
- getString(R.string.account_select_cancel), Toast.LENGTH_SHORT).show();
+ Toast.makeText(getActivity(), getString(R.string.account_select_cancel),
+ Toast.LENGTH_SHORT).show();
} else {
createAccountPicker(accountsForLogin, currentAccount).show();
}
return true;
}
});
-
- // TODO: Depending on the account selection, enable/disable preferences that
- // depend on an account.
}
/**
- * Refreshes the "Sync Now" feature
+ * Enables the Sync preference UI and updates its summary.
*/
- private void refreshSyncNow() {
+ private void enableSyncPreference() {
if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
return;
}
- final Preference syncNowPreference = findPreference(PREF_SYNC_NOW);
- if (syncNowPreference == null) {
+ final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC);
+ syncPreference.setEnabled(true);
+ syncPreference.setSummary(R.string.cloud_sync_summary);
+ }
+
+ /**
+ * Disables the Sync preference UI and updates its summary to indicate
+ * the fact that an account needs to be selected for sync.
+ */
+ private void disableSyncPreference() {
+ if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
return;
}
- final String currentAccount = getCurrentlySelectedAccount();
- if (currentAccount == null) {
- syncNowPreference.setEnabled(false);
- syncNowPreference.setSummary(R.string.sync_now_summary);
- } else {
- syncNowPreference.setEnabled(true);
- syncNowPreference.setSummary(R.string.sync_now_summary_disabled_signed_out);
- }
+ final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC);
+ syncPreference.setEnabled(false);
+ syncPreference.setSummary(R.string.cloud_sync_summary_disabled_signed_out);
}
@Nullable
- private String getCurrentlySelectedAccount() {
- return getSharedPreferences().getString(Settings.PREF_ACCOUNT_NAME, null);
+ String getSignedInAccountName() {
+ return getSharedPreferences().getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
+ }
+
+ boolean isSyncEnabled() {
+ return getSharedPreferences().getBoolean(PREF_ENABLE_CLOUD_SYNC, false);
}
/**
@@ -200,51 +215,51 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
.setTitle(R.string.account_select_title)
.setSingleChoiceItems(accounts, index, null)
- .setPositiveButton(R.string.account_select_ok, mAccountSelectedListener)
+ .setPositiveButton(R.string.account_select_ok, mAccountChangedListener)
.setNegativeButton(R.string.account_select_cancel, null);
if (isSignedIn) {
- builder.setNeutralButton(R.string.account_select_sign_out, mAccountSignedOutListener);
+ builder.setNeutralButton(R.string.account_select_sign_out, mAccountChangedListener);
}
return builder.create();
}
/**
- * Listener for an account being selected from the picker.
- * Persists the account to shared preferences.
- */
- class AccountSelectedListener implements DialogInterface.OnClickListener {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final ListView lv = ((AlertDialog)dialog).getListView();
- final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition());
- getSharedPreferences()
- .edit()
- .putString(Settings.PREF_ACCOUNT_NAME, (String) selectedItem)
- .apply();
- }
- }
-
- /**
- * Listener for sign-out being initiated from from the picker.
- * Removed the account from shared preferences.
+ * Listener for a account selection changes from the picker.
+ * Persists/removes the account to/from shared preferences and sets up sync if required.
*/
- class AccountSignedOutListener implements DialogInterface.OnClickListener {
+ class AccountChangedListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
- getSharedPreferences()
- .edit()
- .remove(Settings.PREF_ACCOUNT_NAME)
- .apply();
+ final String oldAccount = getSignedInAccountName();
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE: // Signed in
+ final ListView lv = ((AlertDialog)dialog).getListView();
+ final String newAccount =
+ (String) lv.getItemAtPosition(lv.getCheckedItemPosition());
+ getSharedPreferences()
+ .edit()
+ .putString(PREF_ACCOUNT_NAME, newAccount)
+ .apply();
+ AccountStateChangedListener.onAccountSignedIn(oldAccount, newAccount);
+ break;
+ case DialogInterface.BUTTON_NEUTRAL: // Signed out
+ AccountStateChangedListener.onAccountSignedOut(oldAccount);
+ getSharedPreferences()
+ .edit()
+ .remove(PREF_ACCOUNT_NAME)
+ .apply();
+ break;
+ }
}
}
/**
- * Listener that initates the process of sync in the background.
+ * Listener that initiates the process of sync in the background.
*/
class SyncNowListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(final Preference preference) {
- BeanstalkManager.getInstance(getActivity() /* context */).requestSync();
+ AccountStateChangedListener.forceSync(getSignedInAccountName());
return true;
}
}
diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
index 3303ab093..d2c9dbbe9 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
@@ -113,6 +113,7 @@ public final class AdvancedSettingsFragment extends SubScreenFragment {
setupKeypressVibrationDurationSettings();
setupKeypressSoundVolumeSettings();
+ setupKeyLongpressTimeoutSettings();
refreshEnablingsOfKeypressSoundAndVibrationSettings();
}
@@ -249,4 +250,43 @@ public final class AdvancedSettingsFragment extends SubScreenFragment {
}
});
}
+
+ private void setupKeyLongpressTimeoutSettings() {
+ final SharedPreferences prefs = getSharedPreferences();
+ final Resources res = getResources();
+ final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
+ Settings.PREF_KEY_LONGPRESS_TIMEOUT);
+ if (pref == null) {
+ return;
+ }
+ pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
+ @Override
+ public void writeValue(final int value, final String key) {
+ prefs.edit().putInt(key, value).apply();
+ }
+
+ @Override
+ public void writeDefaultValue(final String key) {
+ prefs.edit().remove(key).apply();
+ }
+
+ @Override
+ public int readValue(final String key) {
+ return Settings.readKeyLongpressTimeout(prefs, res);
+ }
+
+ @Override
+ public int readDefaultValue(final String key) {
+ return Settings.readDefaultKeyLongpressTimeout(res);
+ }
+
+ @Override
+ public String getValueText(final int value) {
+ return res.getString(R.string.abbreviation_unit_milliseconds, value);
+ }
+
+ @Override
+ public void feedbackValue(final int value) {}
+ });
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
new file mode 100644
index 000000000..c07b60a0b
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.settings;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.DialogPreference;
+import android.preference.Preference;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.compat.ViewCompatUtils;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.TreeSet;
+
+final class CustomInputStylePreference extends DialogPreference
+ implements DialogInterface.OnCancelListener {
+ private static final boolean DEBUG_SUBTYPE_ID = false;
+
+ interface Listener {
+ public void onRemoveCustomInputStyle(CustomInputStylePreference stylePref);
+ public void onSaveCustomInputStyle(CustomInputStylePreference stylePref);
+ public void onAddCustomInputStyle(CustomInputStylePreference stylePref);
+ public SubtypeLocaleAdapter getSubtypeLocaleAdapter();
+ public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter();
+ }
+
+ private static final String KEY_PREFIX = "subtype_pref_";
+ private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new";
+
+ private InputMethodSubtype mSubtype;
+ private InputMethodSubtype mPreviousSubtype;
+
+ private final Listener mProxy;
+ private Spinner mSubtypeLocaleSpinner;
+ private Spinner mKeyboardLayoutSetSpinner;
+
+ public static CustomInputStylePreference newIncompleteSubtypePreference(
+ final Context context, final Listener proxy) {
+ return new CustomInputStylePreference(context, null, proxy);
+ }
+
+ public CustomInputStylePreference(final Context context, final InputMethodSubtype subtype,
+ final Listener proxy) {
+ super(context, null);
+ setDialogLayoutResource(R.layout.additional_subtype_dialog);
+ setPersistent(false);
+ mProxy = proxy;
+ setSubtype(subtype);
+ }
+
+ public void show() {
+ showDialog(null);
+ }
+
+ public final boolean isIncomplete() {
+ return mSubtype == null;
+ }
+
+ public InputMethodSubtype getSubtype() {
+ return mSubtype;
+ }
+
+ public void setSubtype(final InputMethodSubtype subtype) {
+ mPreviousSubtype = mSubtype;
+ mSubtype = subtype;
+ if (isIncomplete()) {
+ setTitle(null);
+ setDialogTitle(R.string.add_style);
+ setKey(KEY_NEW_SUBTYPE);
+ } else {
+ final String displayName =
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
+ setTitle(displayName);
+ setDialogTitle(displayName);
+ setKey(KEY_PREFIX + subtype.getLocale() + "_"
+ + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype));
+ }
+ }
+
+ public void revert() {
+ setSubtype(mPreviousSubtype);
+ }
+
+ public boolean hasBeenModified() {
+ return mSubtype != null && !mSubtype.equals(mPreviousSubtype);
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ final View v = super.onCreateDialogView();
+ mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner);
+ mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter());
+ mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner);
+ mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter());
+ // All keyboard layout names are in the Latin script and thus left to right. That means
+ // the view would align them to the left even if the system locale is RTL, but that
+ // would look strange. To fix this, we align them to the view's start, which will be
+ // natural for any direction.
+ ViewCompatUtils.setTextAlignment(
+ mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START);
+ return v;
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
+ builder.setCancelable(true).setOnCancelListener(this);
+ if (isIncomplete()) {
+ builder.setPositiveButton(R.string.add, this)
+ .setNegativeButton(android.R.string.cancel, this);
+ } else {
+ builder.setPositiveButton(R.string.save, this)
+ .setNeutralButton(android.R.string.cancel, this)
+ .setNegativeButton(R.string.remove, this);
+ final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype);
+ final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype);
+ setSpinnerPosition(mSubtypeLocaleSpinner, localeItem);
+ setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem);
+ }
+ }
+
+ private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) {
+ final SpinnerAdapter adapter = spinner.getAdapter();
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ final Object item = spinner.getItemAtPosition(i);
+ if (item.equals(itemToSelect)) {
+ spinner.setSelection(i);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void onCancel(final DialogInterface dialog) {
+ if (isIncomplete()) {
+ mProxy.onRemoveCustomInputStyle(this);
+ }
+ }
+
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ super.onClick(dialog, which);
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ final boolean isEditing = !isIncomplete();
+ final SubtypeLocaleItem locale =
+ (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem();
+ final KeyboardLayoutSetItem layout =
+ (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
+ final InputMethodSubtype subtype =
+ AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+ locale.mLocaleString, layout.mLayoutName);
+ setSubtype(subtype);
+ notifyChanged();
+ if (isEditing) {
+ mProxy.onSaveCustomInputStyle(this);
+ } else {
+ mProxy.onAddCustomInputStyle(this);
+ }
+ break;
+ case DialogInterface.BUTTON_NEUTRAL:
+ // Nothing to do
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ mProxy.onRemoveCustomInputStyle(this);
+ break;
+ }
+ }
+
+ private static int getSpinnerPosition(final Spinner spinner) {
+ if (spinner == null) return -1;
+ return spinner.getSelectedItemPosition();
+ }
+
+ private static void setSpinnerPosition(final Spinner spinner, final int position) {
+ if (spinner == null || position < 0) return;
+ spinner.setSelection(position);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final Parcelable superState = super.onSaveInstanceState();
+ final Dialog dialog = getDialog();
+ if (dialog == null || !dialog.isShowing()) {
+ return superState;
+ }
+
+ final SavedState myState = new SavedState(superState);
+ myState.mSubtype = mSubtype;
+ myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner);
+ myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner);
+ return myState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(final Parcelable state) {
+ if (!(state instanceof SavedState)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ final SavedState myState = (SavedState) state;
+ super.onRestoreInstanceState(myState.getSuperState());
+ setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos);
+ setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos);
+ setSubtype(myState.mSubtype);
+ }
+
+ static final class SavedState extends Preference.BaseSavedState {
+ InputMethodSubtype mSubtype;
+ int mSubtypeLocaleSelectedPos;
+ int mKeyboardLayoutSetSelectedPos;
+
+ public SavedState(final Parcelable superState) {
+ super(superState);
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mSubtypeLocaleSelectedPos);
+ dest.writeInt(mKeyboardLayoutSetSelectedPos);
+ dest.writeParcelable(mSubtype, 0);
+ }
+
+ public SavedState(final Parcel source) {
+ super(source);
+ mSubtypeLocaleSelectedPos = source.readInt();
+ mKeyboardLayoutSetSelectedPos = source.readInt();
+ mSubtype = (InputMethodSubtype)source.readParcelable(null);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(final Parcel source) {
+ return new SavedState(source);
+ }
+
+ @Override
+ public SavedState[] newArray(final int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> {
+ public final String mLocaleString;
+ private final String mDisplayName;
+
+ public SubtypeLocaleItem(final InputMethodSubtype subtype) {
+ mLocaleString = subtype.getLocale();
+ mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(
+ mLocaleString);
+ }
+
+ // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
+ // to get display name.
+ @Override
+ public String toString() {
+ return mDisplayName;
+ }
+
+ @Override
+ public int compareTo(final SubtypeLocaleItem o) {
+ return mLocaleString.compareTo(o.mLocaleString);
+ }
+ }
+
+ static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> {
+ private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName();
+
+ public SubtypeLocaleAdapter(final Context context) {
+ super(context, android.R.layout.simple_spinner_item);
+ setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ final TreeSet<SubtypeLocaleItem> items = new TreeSet<>();
+ final InputMethodInfo imi = RichInputMethodManager.getInstance()
+ .getInputMethodInfoOfThisIme();
+ final int count = imi.getSubtypeCount();
+ for (int i = 0; i < count; i++) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+ if (DEBUG_SUBTYPE_ID) {
+ Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s",
+ subtype.getLocale(), subtype.hashCode(), subtype.hashCode(),
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)));
+ }
+ if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
+ items.add(new SubtypeLocaleItem(subtype));
+ }
+ }
+ // TODO: Should filter out already existing combinations of locale and layout.
+ addAll(items);
+ }
+ }
+
+ static final class KeyboardLayoutSetItem {
+ public final String mLayoutName;
+ private final String mDisplayName;
+
+ public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
+ mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+ mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype);
+ }
+
+ // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
+ // to get display name.
+ @Override
+ public String toString() {
+ return mDisplayName;
+ }
+ }
+
+ static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
+ public KeyboardLayoutSetAdapter(final Context context) {
+ super(context, android.R.layout.simple_spinner_item);
+ setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ // TODO: Should filter out already existing combinations of locale and layout.
+ for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) {
+ // This is a dummy subtype with NO_LANGUAGE, only for display.
+ final InputMethodSubtype subtype =
+ AdditionalSubtypeUtils.createDummyAdditionalSubtype(
+ SubtypeLocaleUtils.NO_LANGUAGE, layout);
+ add(new KeyboardLayoutSetItem(subtype));
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
index c633fc167..46fcc7106 100644
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
@@ -17,16 +17,12 @@
package com.android.inputmethod.latin.settings;
import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.preference.DialogPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
@@ -39,15 +35,9 @@ 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;
-import android.widget.Spinner;
-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;
@@ -56,19 +46,18 @@ import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.ArrayList;
-import java.util.TreeSet;
-public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
+public final class CustomInputStyleSettingsFragment extends PreferenceFragment
+ implements CustomInputStylePreference.Listener {
private static final String TAG = CustomInputStyleSettingsFragment.class.getSimpleName();
- private static final boolean DEBUG_SUBTYPE_ID = false;
// Note: We would like to turn this debug flag true in order to see what input styles are
// defined in a bug-report.
private static final boolean DEBUG_CUSTOM_INPUT_STYLES = true;
private RichInputMethodManager mRichImm;
private SharedPreferences mPrefs;
- private SubtypeLocaleAdapter mSubtypeLocaleAdapter;
- private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
+ private CustomInputStylePreference.SubtypeLocaleAdapter mSubtypeLocaleAdapter;
+ private CustomInputStylePreference.KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
private boolean mIsAddingNewSubtype;
private AlertDialog mSubtypeEnablerNotificationDialog;
@@ -79,320 +68,6 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
"is_subtype_enabler_notification_dialog_open";
private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
- static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> {
- public final String mLocaleString;
- private final String mDisplayName;
-
- public SubtypeLocaleItem(final InputMethodSubtype subtype) {
- mLocaleString = subtype.getLocale();
- mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(
- mLocaleString);
- }
-
- // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
- // to get display name.
- @Override
- public String toString() {
- return mDisplayName;
- }
-
- @Override
- public int compareTo(final SubtypeLocaleItem o) {
- return mLocaleString.compareTo(o.mLocaleString);
- }
- }
-
- static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> {
- private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName();
-
- public SubtypeLocaleAdapter(final Context context) {
- super(context, android.R.layout.simple_spinner_item);
- setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-
- final TreeSet<SubtypeLocaleItem> items = new TreeSet<>();
- final InputMethodInfo imi = RichInputMethodManager.getInstance()
- .getInputMethodInfoOfThisIme();
- final int count = imi.getSubtypeCount();
- for (int i = 0; i < count; i++) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(i);
- if (DEBUG_SUBTYPE_ID) {
- Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s",
- subtype.getLocale(), subtype.hashCode(), subtype.hashCode(),
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)));
- }
- if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
- items.add(new SubtypeLocaleItem(subtype));
- }
- }
- // TODO: Should filter out already existing combinations of locale and layout.
- addAll(items);
- }
- }
-
- static final class KeyboardLayoutSetItem {
- public final String mLayoutName;
- private final String mDisplayName;
-
- public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
- mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
- mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype);
- }
-
- // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
- // to get display name.
- @Override
- public String toString() {
- return mDisplayName;
- }
- }
-
- static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
- public KeyboardLayoutSetAdapter(final Context context) {
- super(context, android.R.layout.simple_spinner_item);
- setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-
- // TODO: Should filter out already existing combinations of locale and layout.
- for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) {
- // This is a dummy subtype with NO_LANGUAGE, only for display.
- final InputMethodSubtype subtype =
- AdditionalSubtypeUtils.createDummyAdditionalSubtype(
- SubtypeLocaleUtils.NO_LANGUAGE, layout);
- add(new KeyboardLayoutSetItem(subtype));
- }
- }
- }
-
- private interface SubtypeDialogProxy {
- public void onRemovePressed(SubtypePreference subtypePref);
- public void onSavePressed(SubtypePreference subtypePref);
- public void onAddPressed(SubtypePreference subtypePref);
- public SubtypeLocaleAdapter getSubtypeLocaleAdapter();
- public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter();
- }
-
- static final class SubtypePreference extends DialogPreference
- implements DialogInterface.OnCancelListener {
- private static final String KEY_PREFIX = "subtype_pref_";
- private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new";
-
- private InputMethodSubtype mSubtype;
- private InputMethodSubtype mPreviousSubtype;
-
- private final SubtypeDialogProxy mProxy;
- private Spinner mSubtypeLocaleSpinner;
- private Spinner mKeyboardLayoutSetSpinner;
-
- public static SubtypePreference newIncompleteSubtypePreference(final Context context,
- final SubtypeDialogProxy proxy) {
- return new SubtypePreference(context, null, proxy);
- }
-
- public SubtypePreference(final Context context, final InputMethodSubtype subtype,
- final SubtypeDialogProxy proxy) {
- super(context, null);
- setDialogLayoutResource(R.layout.additional_subtype_dialog);
- setPersistent(false);
- mProxy = proxy;
- setSubtype(subtype);
- }
-
- public void show() {
- showDialog(null);
- }
-
- public final boolean isIncomplete() {
- return mSubtype == null;
- }
-
- public InputMethodSubtype getSubtype() {
- return mSubtype;
- }
-
- public void setSubtype(final InputMethodSubtype subtype) {
- mPreviousSubtype = mSubtype;
- mSubtype = subtype;
- if (isIncomplete()) {
- setTitle(null);
- setDialogTitle(R.string.add_style);
- setKey(KEY_NEW_SUBTYPE);
- } else {
- final String displayName =
- SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
- setTitle(displayName);
- setDialogTitle(displayName);
- setKey(KEY_PREFIX + subtype.getLocale() + "_"
- + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype));
- }
- }
-
- public void revert() {
- setSubtype(mPreviousSubtype);
- }
-
- public boolean hasBeenModified() {
- return mSubtype != null && !mSubtype.equals(mPreviousSubtype);
- }
-
- @Override
- protected View onCreateDialogView() {
- final View v = super.onCreateDialogView();
- mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner);
- mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter());
- mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner);
- mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter());
- // All keyboard layout names are in the Latin script and thus left to right. That means
- // the view would align them to the left even if the system locale is RTL, but that
- // would look strange. To fix this, we align them to the view's start, which will be
- // natural for any direction.
- ViewCompatUtils.setTextAlignment(
- mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START);
- return v;
- }
-
- @Override
- protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
- builder.setCancelable(true).setOnCancelListener(this);
- if (isIncomplete()) {
- builder.setPositiveButton(R.string.add, this)
- .setNegativeButton(android.R.string.cancel, this);
- } else {
- builder.setPositiveButton(R.string.save, this)
- .setNeutralButton(android.R.string.cancel, this)
- .setNegativeButton(R.string.remove, this);
- final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype);
- final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype);
- setSpinnerPosition(mSubtypeLocaleSpinner, localeItem);
- setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem);
- }
- }
-
- private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) {
- final SpinnerAdapter adapter = spinner.getAdapter();
- final int count = adapter.getCount();
- for (int i = 0; i < count; i++) {
- final Object item = spinner.getItemAtPosition(i);
- if (item.equals(itemToSelect)) {
- spinner.setSelection(i);
- return;
- }
- }
- }
-
- @Override
- public void onCancel(final DialogInterface dialog) {
- if (isIncomplete()) {
- mProxy.onRemovePressed(this);
- }
- }
-
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- super.onClick(dialog, which);
- switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
- final boolean isEditing = !isIncomplete();
- final SubtypeLocaleItem locale =
- (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem();
- final KeyboardLayoutSetItem layout =
- (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
- final InputMethodSubtype subtype =
- AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
- locale.mLocaleString, layout.mLayoutName);
- setSubtype(subtype);
- notifyChanged();
- if (isEditing) {
- mProxy.onSavePressed(this);
- } else {
- mProxy.onAddPressed(this);
- }
- break;
- case DialogInterface.BUTTON_NEUTRAL:
- // Nothing to do
- break;
- case DialogInterface.BUTTON_NEGATIVE:
- mProxy.onRemovePressed(this);
- break;
- }
- }
-
- private static int getSpinnerPosition(final Spinner spinner) {
- if (spinner == null) return -1;
- return spinner.getSelectedItemPosition();
- }
-
- private static void setSpinnerPosition(final Spinner spinner, final int position) {
- if (spinner == null || position < 0) return;
- spinner.setSelection(position);
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- final Parcelable superState = super.onSaveInstanceState();
- final Dialog dialog = getDialog();
- if (dialog == null || !dialog.isShowing()) {
- return superState;
- }
-
- final SavedState myState = new SavedState(superState);
- myState.mSubtype = mSubtype;
- myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner);
- myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner);
- return myState;
- }
-
- @Override
- protected void onRestoreInstanceState(final Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
-
- final SavedState myState = (SavedState) state;
- super.onRestoreInstanceState(myState.getSuperState());
- setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos);
- setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos);
- setSubtype(myState.mSubtype);
- }
-
- static final class SavedState extends Preference.BaseSavedState {
- InputMethodSubtype mSubtype;
- int mSubtypeLocaleSelectedPos;
- int mKeyboardLayoutSetSelectedPos;
-
- public SavedState(final Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mSubtypeLocaleSelectedPos);
- dest.writeInt(mKeyboardLayoutSetSelectedPos);
- dest.writeParcelable(mSubtype, 0);
- }
-
- public SavedState(final Parcel source) {
- super(source);
- mSubtypeLocaleSelectedPos = source.readInt();
- mKeyboardLayoutSetSelectedPos = source.readInt();
- mSubtype = (InputMethodSubtype)source.readParcelable(null);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR =
- new Parcelable.Creator<SavedState>() {
- @Override
- public SavedState createFromParcel(final Parcel source) {
- return new SavedState(source);
- }
-
- @Override
- public SavedState[] newArray(final int size) {
- return new SavedState[size];
- }
- };
- }
- }
-
public CustomInputStyleSettingsFragment() {
// Empty constructor for fragment generation.
}
@@ -440,8 +115,9 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
final Context context = getActivity();
- mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context);
- mKeyboardLayoutSetAdapter = new KeyboardLayoutSetAdapter(context);
+ mSubtypeLocaleAdapter = new CustomInputStylePreference.SubtypeLocaleAdapter(context);
+ mKeyboardLayoutSetAdapter =
+ new CustomInputStylePreference.KeyboardLayoutSetAdapter(context);
final String prefSubtypes =
Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
@@ -454,7 +130,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
&& savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE);
if (mIsAddingNewSubtype) {
getPreferenceScreen().addPreference(
- SubtypePreference.newIncompleteSubtypePreference(context, mSubtypeProxy));
+ CustomInputStylePreference.newIncompleteSubtypePreference(context, this));
}
super.onActivityCreated(savedInstanceState);
@@ -482,62 +158,60 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
}
}
- private final SubtypeDialogProxy mSubtypeProxy = new SubtypeDialogProxy() {
- @Override
- public void onRemovePressed(final SubtypePreference subtypePref) {
- mIsAddingNewSubtype = false;
- final PreferenceGroup group = getPreferenceScreen();
- group.removePreference(subtypePref);
+ @Override
+ public void onRemoveCustomInputStyle(final CustomInputStylePreference stylePref) {
+ mIsAddingNewSubtype = false;
+ final PreferenceGroup group = getPreferenceScreen();
+ group.removePreference(stylePref);
+ mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
+ }
+
+ @Override
+ public void onSaveCustomInputStyle(final CustomInputStylePreference stylePref) {
+ final InputMethodSubtype subtype = stylePref.getSubtype();
+ if (!stylePref.hasBeenModified()) {
+ return;
+ }
+ if (findDuplicatedSubtype(subtype) == null) {
mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
+ return;
}
- @Override
- public void onSavePressed(final SubtypePreference subtypePref) {
- final InputMethodSubtype subtype = subtypePref.getSubtype();
- if (!subtypePref.hasBeenModified()) {
- return;
- }
- if (findDuplicatedSubtype(subtype) == null) {
- mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
- return;
- }
+ // Saved subtype is duplicated.
+ final PreferenceGroup group = getPreferenceScreen();
+ group.removePreference(stylePref);
+ stylePref.revert();
+ group.addPreference(stylePref);
+ showSubtypeAlreadyExistsToast(subtype);
+ }
- // Saved subtype is duplicated.
- final PreferenceGroup group = getPreferenceScreen();
- group.removePreference(subtypePref);
- subtypePref.revert();
- group.addPreference(subtypePref);
- showSubtypeAlreadyExistsToast(subtype);
+ @Override
+ public void onAddCustomInputStyle(final CustomInputStylePreference stylePref) {
+ mIsAddingNewSubtype = false;
+ final InputMethodSubtype subtype = stylePref.getSubtype();
+ if (findDuplicatedSubtype(subtype) == null) {
+ mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
+ mSubtypePreferenceKeyForSubtypeEnabler = stylePref.getKey();
+ mSubtypeEnablerNotificationDialog = createDialog();
+ mSubtypeEnablerNotificationDialog.show();
+ return;
}
- @Override
- public void onAddPressed(final SubtypePreference subtypePref) {
- mIsAddingNewSubtype = false;
- final InputMethodSubtype subtype = subtypePref.getSubtype();
- if (findDuplicatedSubtype(subtype) == null) {
- mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
- mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey();
- mSubtypeEnablerNotificationDialog = createDialog();
- mSubtypeEnablerNotificationDialog.show();
- return;
- }
-
- // Newly added subtype is duplicated.
- final PreferenceGroup group = getPreferenceScreen();
- group.removePreference(subtypePref);
- showSubtypeAlreadyExistsToast(subtype);
- }
+ // Newly added subtype is duplicated.
+ final PreferenceGroup group = getPreferenceScreen();
+ group.removePreference(stylePref);
+ showSubtypeAlreadyExistsToast(subtype);
+ }
- @Override
- public SubtypeLocaleAdapter getSubtypeLocaleAdapter() {
- return mSubtypeLocaleAdapter;
- }
+ @Override
+ public CustomInputStylePreference.SubtypeLocaleAdapter getSubtypeLocaleAdapter() {
+ return mSubtypeLocaleAdapter;
+ }
- @Override
- public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() {
- return mKeyboardLayoutSetAdapter;
- }
- };
+ @Override
+ public CustomInputStylePreference.KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() {
+ return mKeyboardLayoutSetAdapter;
+ }
private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) {
final Context context = getActivity();
@@ -555,6 +229,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
}
private AlertDialog createDialog() {
+ final String imeId = mRichImm.getInputMethodIdOfThisIme();
final AlertDialog.Builder builder = new AlertDialog.Builder(
DialogUtils.getPlatformDialogThemeContext(getActivity()));
builder.setTitle(R.string.custom_input_styles_title)
@@ -564,7 +239,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
- mRichImm.getInputMethodIdOfThisIme(),
+ imeId,
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -584,8 +259,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
final InputMethodSubtype[] subtypesArray =
AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes);
for (final InputMethodSubtype subtype : subtypesArray) {
- final SubtypePreference pref = new SubtypePreference(
- context, subtype, mSubtypeProxy);
+ final CustomInputStylePreference pref =
+ new CustomInputStylePreference(context, subtype, this);
group.addPreference(pref);
}
}
@@ -596,8 +271,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
final int count = group.getPreferenceCount();
for (int i = 0; i < count; i++) {
final Preference pref = group.getPreference(i);
- if (pref instanceof SubtypePreference) {
- final SubtypePreference subtypePref = (SubtypePreference)pref;
+ if (pref instanceof CustomInputStylePreference) {
+ final CustomInputStylePreference subtypePref = (CustomInputStylePreference)pref;
// We should not save newly adding subtype to preference because it is incomplete.
if (subtypePref.isIncomplete()) continue;
subtypes.add(subtypePref.getSubtype());
@@ -631,8 +306,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
public boolean onOptionsItemSelected(final MenuItem item) {
final int itemId = item.getItemId();
if (itemId == R.id.action_add_style) {
- final SubtypePreference newSubtype =
- SubtypePreference.newIncompleteSubtypePreference(getActivity(), mSubtypeProxy);
+ final CustomInputStylePreference newSubtype =
+ CustomInputStylePreference.newIncompleteSubtypePreference(getActivity(), this);
getPreferenceScreen().addPreference(newSubtype);
newSubtype.show();
mIsAddingNewSubtype = true;
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index 091ca43c6..768cba9b2 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -16,29 +16,36 @@
package com.android.inputmethod.latin.settings;
+/**
+ * Debug settings for the application.
+ *
+ * Note: Even though these settings are stored in the default shared preferences file,
+ * they shouldn't be restored across devices.
+ * If a new key is added here, it should also be blacklisted for restore in
+ * {@link LocalSettingsConstants}.
+ */
public final class DebugSettings {
public static final String PREF_DEBUG_MODE = "debug_mode";
public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY =
"force_physical_keyboard_special_key";
- public static final String PREF_SHOULD_SHOW_LXX_SUGGESTION_UI =
- "pref_should_show_lxx_suggestion_ui";
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_DURATION =
+ "pref_key_preview_dismiss_duration";
public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE =
"pref_key_preview_dismiss_end_x_scale";
public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE =
"pref_key_preview_dismiss_end_y_scale";
public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION =
"pref_key_preview_show_up_duration";
- public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
- "pref_key_preview_dismiss_duration";
+ public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE =
+ "pref_key_preview_show_up_start_x_scale";
+ public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE =
+ "pref_key_preview_show_up_start_y_scale";
+ public static final String PREF_SHOULD_SHOW_LXX_SUGGESTION_UI =
+ "pref_should_show_lxx_suggestion_ui";
public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview";
- public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
private DebugSettings() {
// This class is not publicly instantiable.
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
index e9f8d45aa..475f1def7 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
@@ -73,7 +73,6 @@ public final class DebugSettingsFragment extends SubScreenFragment
dictDumpPreferenceGroup.addPreference(pref);
}
final Resources res = getResources();
- setupKeyLongpressTimeoutSettings();
setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
res.getInteger(R.integer.config_key_preview_show_up_duration));
setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
@@ -163,45 +162,6 @@ public final class DebugSettingsFragment extends SubScreenFragment
}
}
- private void setupKeyLongpressTimeoutSettings() {
- final SharedPreferences prefs = getSharedPreferences();
- final Resources res = getResources();
- final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
- DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT);
- if (pref == null) {
- return;
- }
- pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
- @Override
- public void writeValue(final int value, final String key) {
- prefs.edit().putInt(key, value).apply();
- }
-
- @Override
- public void writeDefaultValue(final String key) {
- prefs.edit().remove(key).apply();
- }
-
- @Override
- public int readValue(final String key) {
- return Settings.readKeyLongpressTimeout(prefs, res);
- }
-
- @Override
- public int readDefaultValue(final String key) {
- return Settings.readDefaultKeyLongpressTimeout(res);
- }
-
- @Override
- public String getValueText(final int value) {
- return res.getString(R.string.abbreviation_unit_milliseconds, value);
- }
-
- @Override
- public void feedbackValue(final int value) {}
- });
- }
-
private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) {
final SharedPreferences prefs = getSharedPreferences();
final Resources res = getResources();
diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
new file mode 100644
index 000000000..c9e9dc8d9
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.settings;
+
+/**
+ * Collection of device specific preference constants.
+ */
+public class LocalSettingsConstants {
+ // Preference file for storing preferences that are tied to a device
+ // and are not backed up.
+ public static final String PREFS_FILE = "local_prefs";
+
+ // Preference key for the current account.
+ // Do not restore.
+ public static final String PREF_ACCOUNT_NAME = "pref_account_name";
+ // Preference key for enabling cloud sync feature.
+ // Do not restore.
+ public static final String PREF_ENABLE_CLOUD_SYNC = "pref_enable_cloud_sync";
+
+ // List of preference keys to skip from being restored by backup agent.
+ // These preferences are tied to a device and hence should not be restored.
+ // e.g. account name.
+ // Ideally they could have been kept in a separate file that wasn't backed up
+ // however the preference UI currently only deals with the default
+ // shared preferences which makes it non-trivial to move these out to
+ // a different shared preferences file.
+ public static final String[] PREFS_TO_SKIP_RESTORING = new String[] {
+ PREF_ACCOUNT_NAME,
+ PREF_ENABLE_CLOUD_SYNC,
+ // The debug settings are not restored on a new device.
+ // If a feature relies on these, it should ensure that the defaults are
+ // correctly set for it to work on a new device.
+ DebugSettings.PREF_DEBUG_MODE,
+ DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH,
+ DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY,
+ DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS,
+ DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
+ DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
+ DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
+ DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
+ DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
+ DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
+ DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI,
+ DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW
+ };
+}
diff --git a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java
index b073c50a4..b71f8829b 100644
--- a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java
@@ -38,5 +38,6 @@ public final class MultiLingualSettingsFragment extends SubScreenFragment {
removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY);
removePreference(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST);
}
+ AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(getActivity(), this);
}
}
diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
index 31a20c4db..7603dbba5 100644
--- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
+++ b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
@@ -22,7 +22,8 @@ public class NativeSuggestOptions {
private static final int USE_FULL_EDIT_DISTANCE = 1;
private static final int BLOCK_OFFENSIVE_WORDS = 2;
private static final int SPACE_AWARE_GESTURE_ENABLED = 3;
- private static final int OPTIONS_SIZE = 4;
+ private static final int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4;
+ private static final int OPTIONS_SIZE = 5;
private final int[] mOptions = new int[OPTIONS_SIZE
+ AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
@@ -43,6 +44,12 @@ public class NativeSuggestOptions {
setBooleanOption(SPACE_AWARE_GESTURE_ENABLED, value);
}
+ public void setWeightForLocale(final float value) {
+ // We're passing this option as a fixed point value, in thousands. This is decoded in
+ // native code by SuggestOptions#weightForLocale().
+ setIntegerOption(WEIGHT_FOR_LOCALE_IN_THOUSANDS, (int) (value * 1000));
+ }
+
public void setAdditionalFeaturesOptions(final int[] additionalOptions) {
if (additionalOptions == null) {
return;
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 84596b4ad..391fc1982 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -93,8 +93,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_GESTURE_INPUT = "gesture_input";
public static final String PREF_VIBRATION_DURATION_SETTINGS =
"pref_vibration_duration_settings";
- public static final String PREF_KEYPRESS_SOUND_VOLUME =
- "pref_keypress_sound_volume";
+ public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume";
+ public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
"pref_gesture_floating_preview_text";
@@ -106,8 +106,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
- public static final String PREF_ACCOUNT_NAME = "pref_account_name";
-
// This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
// This is being used only for the backward compatibility.
private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
@@ -319,7 +317,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static int readKeyLongpressTimeout(final SharedPreferences prefs,
final Resources res) {
final int milliseconds = prefs.getInt(
- DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
+ PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
: readDefaultKeyLongpressTimeout(res);
}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 8c4801798..6c21accf6 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -56,6 +56,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
final Preference accountsPreference = findPreference(Settings.SCREEN_ACCOUNTS);
preferenceScreen.removePreference(accountsPreference);
}
+ AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(getActivity(), this);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index ce8a0ab9c..660b4e095 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -64,7 +64,6 @@ public class SettingsValues {
public final boolean mSoundOn;
public final boolean mKeyPreviewPopupOn;
public final boolean mShowsVoiceInputKey;
- public final String mAccountName;
public final boolean mIncludesOtherImesInLanguageSwitchList;
public final boolean mShowsLanguageSwitchKey;
public final boolean mUseContactsDict;
@@ -141,7 +140,6 @@ public class SettingsValues {
mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res)
&& mInputAttributes.mShouldShowVoiceInputKey
&& SubtypeSwitcher.getInstance().isShortcutImeEnabled();
- mAccountName = prefs.getString(Settings.PREF_ACCOUNT_NAME, null);
final String autoCorrectionThresholdRawValue = prefs.getString(
Settings.PREF_AUTO_CORRECTION_THRESHOLD,
res.getString(R.string.auto_correction_threshold_mode_index_modest));
@@ -385,8 +383,6 @@ public class SettingsValues {
sb.append("" + mKeyPreviewPopupOn);
sb.append("\n mShowsVoiceInputKey = ");
sb.append("" + mShowsVoiceInputKey);
- sb.append("\n mAccountName = ");
- sb.append("" + mAccountName);
sb.append("\n mIncludesOtherImesInLanguageSwitchList = ");
sb.append("" + mIncludesOtherImesInLanguageSwitchList);
sb.append("\n mShowsLanguageSwitchKey = ");
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index 54562f39d..c3b30dcb4 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -42,6 +42,8 @@ import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils;
import java.util.ArrayList;
+import javax.annotation.Nonnull;
+
// TODO: Use Fragment to implement welcome screen and setup steps.
public final class SetupWizardActivity extends Activity implements View.OnClickListener {
static final String TAG = SetupWizardActivity.class.getSimpleName();
@@ -82,7 +84,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
private final InputMethodManager mImmInHandler;
- public SettingsPoolingHandler(final SetupWizardActivity ownerInstance,
+ public SettingsPoolingHandler(@Nonnull final SetupWizardActivity ownerInstance,
final InputMethodManager imm) {
super(ownerInstance);
mImmInHandler = imm;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index d55939971..7b66bbb75 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -321,18 +321,6 @@ final class SuggestionStripLayoutHelper {
} else {
color = mColorSuggested;
}
- if (DebugFlags.DEBUG_ENABLED && suggestedWords.size() > 1) {
- // If we auto-correct, then the autocorrection is in slot 0 and the typed word
- // is in slot 1.
- if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION
- && suggestedWords.mWillAutoCorrect
- && AutoCorrectionUtils.shouldBlockAutoCorrectionBySafetyNet(
- suggestedWords.getLabel(SuggestedWords.INDEX_OF_AUTO_CORRECTION),
- suggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD))) {
- return 0xFFFF0000;
- }
- }
-
if (suggestedWords.mIsObsoleteSuggestions && !isTypedWord) {
return applyAlpha(color, mAlphaObsoleted);
}
@@ -553,12 +541,12 @@ final class SuggestionStripLayoutHelper {
return countInStrip;
}
- public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) {
- final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent()
- .mShouldShowLxxSuggestionUi;
+ public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip,
+ final boolean shouldShowWordToSave) {
+ final boolean showsHintWithWord = shouldShowWordToSave
+ || !Settings.getInstance().getCurrent().mShouldShowLxxSuggestionUi;
final int stripWidth = addToDictionaryStrip.getWidth();
- final int width = shouldShowUiToAcceptTypedWord ? stripWidth
- : stripWidth - mDividerWidth - mPadding * 2;
+ final int width = stripWidth - (showsHintWithWord ? mDividerWidth + mPadding * 2 : 0);
final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
wordView.setTextColor(mColorTypedWord);
@@ -569,7 +557,7 @@ final class SuggestionStripLayoutHelper {
wordView.setText(wordToSave);
wordView.setTextScaleX(wordScaleX);
setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
- final int wordVisibility = shouldShowUiToAcceptTypedWord ? View.GONE : View.VISIBLE;
+ final int wordVisibility = showsHintWithWord ? View.VISIBLE : View.GONE;
wordView.setVisibility(wordVisibility);
addToDictionaryStrip.findViewById(R.id.word_to_save_divider).setVisibility(wordVisibility);
@@ -579,12 +567,7 @@ final class SuggestionStripLayoutHelper {
final float hintWeight;
final TextView hintView = (TextView)addToDictionaryStrip.findViewById(
R.id.hint_add_to_dictionary);
- if (shouldShowUiToAcceptTypedWord) {
- hintText = res.getText(R.string.hint_add_to_dictionary_without_word);
- hintWidth = width;
- hintWeight = 1.0f;
- hintView.setGravity(Gravity.CENTER);
- } else {
+ if (showsHintWithWord) {
final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip)
== ViewCompat.LAYOUT_DIRECTION_RTL);
final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW;
@@ -595,6 +578,11 @@ final class SuggestionStripLayoutHelper {
hintWidth = width - wordWidth;
hintWeight = 1.0f - mCenterSuggestionWeight;
hintView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
+ } else {
+ hintText = res.getText(R.string.hint_add_to_dictionary_without_word);
+ hintWidth = width;
+ hintWeight = 1.0f;
+ hintView.setGravity(Gravity.CENTER);
}
hintView.setTextColor(mColorAutoCorrect);
final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e40fd8800..789d549d7 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -231,8 +231,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return mStripVisibilityGroup.isShowingAddToDictionaryStrip();
}
- public void showAddToDictionaryHint(final String word) {
- mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip);
+ public void showAddToDictionaryHint(final String word, final boolean shouldShowWordToSave) {
+ mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, shouldShowWordToSave);
// {@link TextView#setTag()} is used to hold the word to be added to dictionary. The word
// will be extracted at {@link #onClick(View)}.
mAddToDictionaryStrip.setTag(word);
@@ -501,7 +501,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return;
}
final Object tag = view.getTag();
- // {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}.
+ // {@link String} tag is set at {@link #suggestAddingToDictionary(String,CharSequence)}.
if (tag instanceof String) {
final String wordToSave = (String)tag;
mListener.addWordToUserDictionary(wordToSave);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
index 52708455e..5c86a02af 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
@@ -22,7 +22,7 @@ import com.android.inputmethod.latin.SuggestedWords;
* An object that gives basic control of a suggestion strip and some info on it.
*/
public interface SuggestionStripViewAccessor {
- public void showAddToDictionaryHint(final String word);
+ public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip);
public boolean isShowingAddToDictionaryHint();
public void dismissAddToDictionaryHint();
public void setNeutralSuggestionStrip();
diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
index 156fcf57c..cba769521 100644
--- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
@@ -52,41 +52,9 @@ public final class AutoCorrectionUtils {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return !shouldBlockAutoCorrectionBySafetyNet(consideredWord, suggestion.mWord);
+ return true;
}
}
return false;
}
-
- // TODO: Resolve the inconsistencies between the native auto correction algorithms and
- // this safety net
- public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
- final String suggestion) {
- // Safety net for auto correction.
- // Actually if we hit this safety net, it's a bug.
- // If user selected aggressive auto correction mode, there is no need to use the safety
- // net.
- // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
- // we should not use net because relatively edit distance can be big.
- final int typedWordLength = typedWord.length();
- if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) {
- return false;
- }
- final int maxEditDistanceOfNativeDictionary = (typedWordLength / 2) + 1;
- final int distance = BinaryDictionaryUtils.editDistance(typedWord, suggestion);
- if (DBG) {
- Log.d(TAG, "Autocorrected edit distance = " + distance
- + ", " + maxEditDistanceOfNativeDictionary);
- }
- if (distance > maxEditDistanceOfNativeDictionary) {
- if (DBG) {
- Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
- Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
- + "Turning off auto-correction.");
- }
- return true;
- } else {
- return false;
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
index 5d7deba15..ce25fe6a4 100644
--- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
@@ -43,7 +43,6 @@ public final class BinaryDictionaryUtils {
private static native boolean createEmptyDictFileNative(String filePath, long dictVersion,
String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray);
private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
- private static native int editDistanceNative(int[] before, int[] after);
private static native int setCurrentTimeForTestNative(int currentTime);
public static DictionaryHeader getHeader(final File dictFile)
@@ -112,14 +111,6 @@ public final class BinaryDictionaryUtils {
StringUtils.toCodePointArray(after), score);
}
- public static int editDistance(final String before, final String after) {
- if (before == null || after == null) {
- throw new IllegalArgumentException();
- }
- return editDistanceNative(StringUtils.toCodePointArray(before),
- StringUtils.toCodePointArray(after));
- }
-
/**
* Control the current time to be used in the native code. If currentTime >= 0, this method sets
* the current time and gets into test mode.
diff --git a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
index 9dc0524a2..e05618901 100644
--- a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
@@ -16,10 +16,12 @@
package com.android.inputmethod.latin.utils;
+import android.annotation.TargetApi;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.inputmethodservice.ExtractEditText;
import android.inputmethodservice.InputMethodService;
+import android.os.Build;
import android.text.Layout;
import android.text.Spannable;
import android.view.View;
@@ -27,6 +29,12 @@ import android.view.ViewParent;
import android.view.inputmethod.CursorAnchorInfo;
import android.widget.TextView;
+import com.android.inputmethod.compat.BuildCompatUtils;
+import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
/**
* This class allows input methods to extract {@link CursorAnchorInfo} directly from the given
* {@link TextView}. This is useful and even necessary to support full-screen mode where the default
@@ -77,13 +85,32 @@ public final class CursorAnchorInfoUtils {
}
/**
+ * Extracts {@link CursorAnchorInfoCompatWrapper} from the given {@link TextView}.
+ * @param textView the target text view from which {@link CursorAnchorInfoCompatWrapper} is to
+ * be extracted.
+ * @return the {@link CursorAnchorInfoCompatWrapper} object based on the current layout.
+ * {@code null} if {@code Build.VERSION.SDK_INT} is 20 or prior or {@link TextView} is not
+ * ready to provide layout information.
+ */
+ @Nullable
+ public static CursorAnchorInfoCompatWrapper extractFromTextView(
+ @Nonnull final TextView textView) {
+ if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) {
+ return null;
+ }
+ return CursorAnchorInfoCompatWrapper.wrap(extractFromTextViewInternal(textView));
+ }
+
+ /**
* Returns {@link CursorAnchorInfo} from the given {@link TextView}.
* @param textView the target text view from which {@link CursorAnchorInfo} is to be extracted.
* @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it
* is not feasible.
*/
- public static CursorAnchorInfo getCursorAnchorInfo(final TextView textView) {
- Layout layout = textView.getLayout();
+ @TargetApi(BuildCompatUtils.VERSION_CODES_LXX)
+ @Nullable
+ private static CursorAnchorInfo extractFromTextViewInternal(@Nonnull final TextView textView) {
+ final Layout layout = textView.getLayout();
if (layout == null) {
return null;
}
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 249478785..e29aabacd 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -230,6 +230,7 @@ public class DictionaryInfoUtils {
/**
* Helper method to return a dictionary res id for a locale, or 0 if none.
+ * @param res resources for the app
* @param locale dictionary locale
* @return main dictionary resource id
*/
@@ -258,6 +259,7 @@ public class DictionaryInfoUtils {
/**
* Returns a main dictionary resource id
+ * @param res resources for the app
* @param locale dictionary locale
* @return main dictionary resource id
*/
@@ -283,6 +285,25 @@ public class DictionaryInfoUtils {
BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.getLanguage().toString();
}
+ /**
+ * Returns whether a main dictionary is readily available for this locale.
+ *
+ * This does not query the content provider.
+ *
+ * @param context context to open files upon
+ * @param locale dictionary locale
+ * @return true if a dictionary is available right away, false otherwise
+ */
+ public static boolean hasReadilyAvailableMainDictionaryForLocale(final Context context,
+ final Locale locale) {
+ final Resources res = context.getResources();
+ if (0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale)) {
+ return true;
+ }
+ final String fileName = getCacheFileName(getMainDictId(locale), locale.toString(), context);
+ return new File(fileName).exists();
+ }
+
public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file) {
return getDictionaryFileHeaderOrNull(file, 0, file.length());
}
diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
index dd6fac671..9a5be99b3 100644
--- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
+++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
@@ -21,21 +21,22 @@ import android.os.Looper;
import java.lang.ref.WeakReference;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
public class LeakGuardHandlerWrapper<T> extends Handler {
private final WeakReference<T> mOwnerInstanceRef;
- public LeakGuardHandlerWrapper(final T ownerInstance) {
+ public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance) {
this(ownerInstance, Looper.myLooper());
}
- public LeakGuardHandlerWrapper(final T ownerInstance, final Looper looper) {
+ public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance, final Looper looper) {
super(looper);
- if (ownerInstance == null) {
- throw new NullPointerException("ownerInstance is null");
- }
mOwnerInstanceRef = new WeakReference<>(ownerInstance);
}
+ @Nullable
public T getOwnerInstance() {
return mOwnerInstanceRef.get();
}