aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java47
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtype.java12
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java14
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java13
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java49
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java4
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java2
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java22
-rw-r--r--java/src/com/android/inputmethod/latin/LocaleUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/NativeSuggestOptions.java53
-rw-r--r--java/src/com/android/inputmethod/latin/ResourceUtils.java81
-rw-r--r--java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java11
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsFragment.java17
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java8
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java90
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java5
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java5
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java126
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/AccountUtils.java19
-rw-r--r--java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java2
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java556
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java544
22 files changed, 981 insertions, 701 deletions
diff --git a/java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java b/java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java
new file mode 100644
index 000000000..0fdaea50c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethodcommon.InputMethodSettingsFragment;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * Utility class for managing additional features settings.
+ */
+public class AdditionalFeaturesSettingUtils {
+ public static final int ADDITIONAL_FEATURES_SETTINGS_SIZE = 0;
+
+ private AdditionalFeaturesSettingUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static void addAdditionalFeaturesPreferences(
+ final Context context, final InputMethodSettingsFragment settingsFragment) {
+ // do nothing.
+ }
+
+ public static void readAdditionalFeaturesPreferencesIntoArray(
+ final SharedPreferences prefs, final int[] additionalFeaturesPreferences) {
+ // do nothing.
+ }
+
+ public static int[] getAdditionalNativeSuggestOptions() {
+ return Settings.getInstance().getCurrent().mAdditionalFeaturesSettingValues;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index 99b95ea98..85b14d849 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -49,13 +49,14 @@ public final class AdditionalSubtype {
&& SubtypeLocale.isExceptionalLocale(localeString)) {
final String layoutDisplayName = SubtypeLocale.getKeyboardLayoutSetDisplayName(
keyboardLayoutSetName);
- layoutDisplayNameExtraValue = StringUtils.appendToCsvIfNotExists(
+ layoutDisplayNameExtraValue = StringUtils.appendToCommaSplittableTextIfNotExists(
UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + layoutDisplayName, extraValue);
} else {
layoutDisplayNameExtraValue = extraValue;
}
- final String additionalSubtypeExtraValue = StringUtils.appendToCsvIfNotExists(
- IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
+ final String additionalSubtypeExtraValue =
+ StringUtils.appendToCommaSplittableTextIfNotExists(
+ IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
final int nameId = SubtypeLocale.getSubtypeNameId(localeString, keyboardLayoutSetName);
return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
localeString, KEYBOARD_MODE,
@@ -66,8 +67,9 @@ public final class AdditionalSubtype {
final String localeString = subtype.getLocale();
final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
- final String extraValue = StringUtils.removeFromCsvIfExists(layoutExtraValue,
- StringUtils.removeFromCsvIfExists(IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
+ final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists(
+ layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists(
+ IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
final String basePrefSubtype = localeString + LOCALE_AND_LAYOUT_SEPARATOR
+ keyboardLayoutSetName;
return extraValue.isEmpty() ? basePrefSubtype
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index fa35922b0..86be4295a 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -32,12 +32,13 @@ public final class AutoCorrection {
// Purely static class: can't instantiate.
}
- public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries,
- final String word, final boolean ignoreCase) {
+ public static boolean isValidWord(final Suggest suggest, final String word,
+ final boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
return false;
}
- final String lowerCasedWord = word.toLowerCase();
+ final ConcurrentHashMap<String, Dictionary> dictionaries = suggest.getUnigramDictionaries();
+ final String lowerCasedWord = word.toLowerCase(suggest.mLocale);
for (final String key : dictionaries.keySet()) {
final Dictionary dictionary = dictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
@@ -73,13 +74,6 @@ public final class AutoCorrection {
return maxFreq;
}
- // Returns true if this is in any of the dictionaries.
- public static boolean isInTheDictionary(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final String word, final boolean ignoreCase) {
- return isValidWord(dictionaries, word, ignoreCase);
- }
-
public static boolean suggestionExceedsAutoCorrectionThreshold(
final SuggestedWordInfo suggestion, final String consideredWord,
final float autoCorrectionThreshold) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 4fc1919dc..aad129d76 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -45,7 +45,7 @@ public final class BinaryDictionary extends Dictionary {
private final int[] mOutputScores = new int[MAX_RESULTS];
private final int[] mOutputTypes = new int[MAX_RESULTS];
- private final boolean mUseFullEditDistance;
+ private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
private final SparseArray<DicTraverseSession> mDicTraverseSessions =
CollectionUtils.newSparseArray();
@@ -79,7 +79,7 @@ public final class BinaryDictionary extends Dictionary {
final boolean useFullEditDistance, final Locale locale, final String dictType) {
super(dictType);
mLocale = locale;
- mUseFullEditDistance = useFullEditDistance;
+ mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
loadDictionary(filename, offset, length);
}
@@ -94,7 +94,7 @@ public final class BinaryDictionary extends Dictionary {
private static native int getSuggestionsNative(long dict, long proximityInfo,
long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
- boolean isGesture, int[] prevWordCodePointArray, boolean useFullEditDistance,
+ int[] suggestOptions, int[] prevWordCodePointArray,
int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes);
private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
private static native int editDistanceNative(int[] before, int[] after);
@@ -135,12 +135,15 @@ public final class BinaryDictionary extends Dictionary {
final InputPointers ips = composer.getInputPointers();
final int inputSize = isGesture ? ips.getPointerSize() : composerSize;
+ mNativeSuggestOptions.setIsGesture(isGesture);
+ mNativeSuggestOptions.setAdditionalFeaturesOptions(
+ AdditionalFeaturesSettingUtils.getAdditionalNativeSuggestOptions());
// proximityInfo and/or prevWordForBigrams may not be null.
final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
getTraverseSession(sessionId).getSession(), ips.getXCoordinates(),
ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints,
- inputSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
- mUseFullEditDistance, mOutputCodePoints, mOutputScores, mSpaceIndices,
+ inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(),
+ prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices,
mOutputTypes);
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
for (int j = 0; j < count; ++j) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index a9b58de44..c038db87c 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -32,6 +32,7 @@ import com.android.inputmethod.latin.DictionaryInfoUtils.DictionaryInfo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -319,20 +320,12 @@ public final class BinaryDictionaryFileDumper {
// Try the next method.
} finally {
// Ignore exceptions while closing files.
- try {
- if (null != afd) afd.close();
- if (null != inputStream) inputStream.close();
- if (null != uncompressedStream) uncompressedStream.close();
- if (null != decryptedStream) decryptedStream.close();
- if (null != bufferedInputStream) bufferedInputStream.close();
- } catch (Exception e) {
- Log.e(TAG, "Exception while closing a file descriptor", e);
- }
- try {
- if (null != bufferedOutputStream) bufferedOutputStream.close();
- } catch (Exception e) {
- Log.e(TAG, "Exception while closing a file", e);
- }
+ closeAssetFileDescriptorAndReportAnyException(afd);
+ closeCloseableAndReportAnyException(inputStream);
+ closeCloseableAndReportAnyException(uncompressedStream);
+ closeCloseableAndReportAnyException(decryptedStream);
+ closeCloseableAndReportAnyException(bufferedInputStream);
+ closeCloseableAndReportAnyException(bufferedOutputStream);
}
}
@@ -352,6 +345,26 @@ public final class BinaryDictionaryFileDumper {
}
}
+ // Ideally the two following methods should be merged, but AssetFileDescriptor does not
+ // implement Closeable although it does implement #close(), and Java does not have
+ // structural typing.
+ private static void closeAssetFileDescriptorAndReportAnyException(
+ final AssetFileDescriptor file) {
+ try {
+ if (null != file) file.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while closing a file", e);
+ }
+ }
+
+ private static void closeCloseableAndReportAnyException(final Closeable file) {
+ try {
+ if (null != file) file.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while closing a file", e);
+ }
+ }
+
/**
* Queries a content provider for word list data for some locale and cache the returned files
*
@@ -363,8 +376,14 @@ public final class BinaryDictionaryFileDumper {
*/
public static void cacheWordListsFromContentProvider(final Locale locale,
final Context context, final boolean hasDefaultWordList) {
- final ContentProviderClient providerClient = context.getContentResolver().
+ final ContentProviderClient providerClient;
+ try {
+ providerClient = context.getContentResolver().
acquireContentProviderClient(getProviderUriBuilder("").build());
+ } catch (final SecurityException e) {
+ Log.e(TAG, "No permission to communicate with the dictionary provider", e);
+ return;
+ }
if (null == providerClient) {
Log.e(TAG, "Can't establish communication with the dictionary provider");
return;
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 86bb25562..64c14d32f 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -215,10 +215,6 @@ public final class Constants {
}
}
- // Constants for CSV parsing.
- public static final char CSV_SEPARATOR = ',';
- public static final char CSV_ESCAPE = '\\';
-
private Constants() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index dd58db575..1f673e9b0 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -199,6 +199,6 @@ public final class InputAttributes {
if (editorInfo == null) return false;
final String findingKey = (packageName != null) ? packageName + "." + key
: key;
- return StringUtils.containsInCsv(findingKey, editorInfo.privateImeOptions);
+ return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions);
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index c464a7067..c9a42a3a4 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -480,6 +480,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final InputAttributes inputAttributes =
new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
mSettings.loadSettings(locale, inputAttributes);
+ AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(mSettings.getCurrent());
// May need to reset the contacts dictionary depending on the user settings.
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
@@ -854,6 +855,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// Remove pending messages related to update suggestions
mHandler.cancelUpdateSuggestionStrip();
+ if (mWordComposer.isComposingWord()) mConnection.finishComposingText();
resetComposingState(true /* alsoResetLastComposedWord */);
// Notify ResearchLogger
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -2020,9 +2022,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Returns true if we did an autocorrection, false otherwise.
private boolean handleSeparator(final int primaryCode, final int x, final int y,
final int spaceState) {
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
- }
boolean didAutoCorrect = false;
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
@@ -2046,6 +2045,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSettings.getCurrent().isUsuallyPrecededBySpace(primaryCode)) {
promotePhantomSpace();
}
+ if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
+ }
sendKeyCodePoint(primaryCode);
if (Constants.CODE_SPACE == primaryCode) {
@@ -2368,9 +2370,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
final boolean showingAddToDictionaryHint =
- SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind && mSuggest != null
- // If the suggestion is not in the dictionary, the hint should be shown.
- && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
+ (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
+ || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
+ && mSuggest != null
+ // If the suggestion is not in the dictionary, the hint should be shown.
+ && !AutoCorrection.isValidWord(mSuggest, suggestion, true);
if (mSettings.isInternal()) {
Stats.onSeparator((char)Constants.CODE_SPACE,
@@ -2585,8 +2589,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_revertCommit(committedWord, originallyTypedWord,
mWordComposer.isBatchMode(), mLastComposedWord.mSeparatorString);
- ResearchLogger.getInstance().uncommitCurrentLogUnit(committedWord,
- true /* dumpCurrentLogUnit */);
}
// Don't restart suggestion yet. We'll restart if the user deletes the
// separator.
@@ -2599,10 +2601,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void promotePhantomSpace() {
if (mSettings.getCurrent().shouldInsertSpacesAutomatically()
&& !mConnection.textBeforeCursorLooksLikeURL()) {
- sendKeyCodePoint(Constants.CODE_SPACE);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_promotePhantomSpace();
}
+ sendKeyCodePoint(Constants.CODE_SPACE);
}
}
@@ -2703,7 +2705,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
mSubtypeSwitcher.onNetworkStateChanged(intent);
} else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- mKeyboardSwitcher.onRingerModeChanged();
+ AudioAndHapticFeedbackManager.getInstance().onRingerModeChanged();
}
}
};
diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java
index 5fde8158a..a1e40502e 100644
--- a/java/src/com/android/inputmethod/latin/LocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java
@@ -148,7 +148,7 @@ public final class LocaleUtils {
public static String getMatchLevelSortedString(int matchLevel) {
// This works because the match levels are 0~99 (actually 0~30)
// Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel
- return String.format("%02d", MATCH_LEVEL_MAX - matchLevel);
+ return String.format(Locale.ROOT, "%02d", MATCH_LEVEL_MAX - matchLevel);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java
new file mode 100644
index 000000000..291551301
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+public class NativeSuggestOptions {
+ // Need to update suggest_options.h when you add, remove or reorder options.
+ private static final int IS_GESTURE = 0;
+ private static final int USE_FULL_EDIT_DISTANCE = 1;
+ private static final int OPTIONS_SIZE = 2;
+
+ private final int[] mOptions = new int[OPTIONS_SIZE
+ + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+
+ public void setIsGesture(final boolean value) {
+ setBooleanOption(IS_GESTURE, value);
+ }
+
+ public void setUseFullEditDistance(final boolean value) {
+ setBooleanOption(USE_FULL_EDIT_DISTANCE, value);
+ }
+
+ public void setAdditionalFeaturesOptions(final int[] additionalOptions) {
+ for (int i = 0; i < additionalOptions.length; i++) {
+ setIntegerOption(OPTIONS_SIZE + i, additionalOptions[i]);
+ }
+ }
+
+ public int[] getOptions() {
+ return mOptions;
+ }
+
+ private void setBooleanOption(final int key, final boolean value) {
+ mOptions[key] = value ? 1 : 0;
+ }
+
+ private void setIntegerOption(final int key, final int value) {
+ mOptions[key] = value;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java
index a9fba5348..0eb8b4f09 100644
--- a/java/src/com/android/inputmethod/latin/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java
@@ -27,6 +27,7 @@ import com.android.inputmethod.annotations.UsedForTesting;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.regex.PatternSyntaxException;
public final class ResourceUtils {
private static final String TAG = ResourceUtils.class.getSimpleName();
@@ -83,22 +84,39 @@ public final class ResourceUtils {
return overrideValue;
}
- final String defaultValue = findDefaultConstant(overrideArray);
- // The defaultValue might be an empty string.
- if (defaultValue == null) {
- Log.w(TAG, "Couldn't find override value nor default value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " build=" + sBuildKeyValuesDebugString);
- } else {
- Log.i(TAG, "Found default value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " build=" + sBuildKeyValuesDebugString
- + " default=" + defaultValue);
+ String defaultValue = null;
+ try {
+ defaultValue = findDefaultConstant(overrideArray);
+ // The defaultValue might be an empty string.
+ if (defaultValue == null) {
+ Log.w(TAG, "Couldn't find override value nor default value:"
+ + " resource="+ res.getResourceEntryName(overrideResId)
+ + " build=" + sBuildKeyValuesDebugString);
+ } else {
+ Log.i(TAG, "Found default value:"
+ + " resource="+ res.getResourceEntryName(overrideResId)
+ + " build=" + sBuildKeyValuesDebugString
+ + " default=" + defaultValue);
+ }
+ } catch (final DeviceOverridePatternSyntaxError e) {
+ Log.w(TAG, "Syntax error, ignored", e);
}
sDeviceOverrideValueMap.put(key, defaultValue);
return defaultValue;
}
+ @SuppressWarnings("serial")
+ static class DeviceOverridePatternSyntaxError extends Exception {
+ public DeviceOverridePatternSyntaxError(final String message, final String expression) {
+ this(message, expression, null);
+ }
+
+ public DeviceOverridePatternSyntaxError(final String message, final String expression,
+ final Throwable throwable) {
+ super(message + ": " + expression, throwable);
+ }
+ }
+
/**
* Find the condition that fulfills specified key value pairs from an array of
* "condition,constant", and return the corresponding string constant. A condition is
@@ -123,10 +141,12 @@ public final class ResourceUtils {
if (conditionConstantArray == null || keyValuePairs == null) {
return null;
}
+ String foundValue = null;
for (final String conditionConstant : conditionConstantArray) {
final int posComma = conditionConstant.indexOf(',');
if (posComma < 0) {
- throw new RuntimeException("Array element has no comma: " + conditionConstant);
+ Log.w(TAG, "Array element has no comma: " + conditionConstant);
+ continue;
}
final String condition = conditionConstant.substring(0, posComma);
if (condition.isEmpty()) {
@@ -134,44 +154,59 @@ public final class ResourceUtils {
// {@link #findConstantForDefault(String[])}.
continue;
}
- if (fulfillsCondition(keyValuePairs, condition)) {
- return conditionConstant.substring(posComma + 1);
+ try {
+ if (fulfillsCondition(keyValuePairs, condition)) {
+ // Take first match
+ if (foundValue == null) {
+ foundValue = conditionConstant.substring(posComma + 1);
+ }
+ // And continue walking through all conditions.
+ }
+ } catch (final DeviceOverridePatternSyntaxError e) {
+ Log.w(TAG, "Syntax error, ignored", e);
}
}
- return null;
+ return foundValue;
}
private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs,
- final String condition) {
+ final String condition) throws DeviceOverridePatternSyntaxError {
final String[] patterns = condition.split(":");
// Check all patterns in a condition are true
+ boolean matchedAll = true;
for (final String pattern : patterns) {
final int posEqual = pattern.indexOf('=');
if (posEqual < 0) {
- throw new RuntimeException("Pattern has no '=': " + condition);
+ throw new DeviceOverridePatternSyntaxError("Pattern has no '='", condition);
}
final String key = pattern.substring(0, posEqual);
final String value = keyValuePairs.get(key);
if (value == null) {
- throw new RuntimeException("Found unknown key: " + condition);
+ throw new DeviceOverridePatternSyntaxError("Unknown key", condition);
}
final String patternRegexpValue = pattern.substring(posEqual + 1);
- if (!value.matches(patternRegexpValue)) {
- return false;
+ try {
+ if (!value.matches(patternRegexpValue)) {
+ matchedAll = false;
+ // And continue walking through all patterns.
+ }
+ } catch (final PatternSyntaxException e) {
+ throw new DeviceOverridePatternSyntaxError("Syntax error", condition, e);
}
}
- return true;
+ return matchedAll;
}
@UsedForTesting
- static String findDefaultConstant(final String[] conditionConstantArray) {
+ static String findDefaultConstant(final String[] conditionConstantArray)
+ throws DeviceOverridePatternSyntaxError {
if (conditionConstantArray == null) {
return null;
}
for (final String condition : conditionConstantArray) {
final int posComma = condition.indexOf(',');
if (posComma < 0) {
- throw new RuntimeException("Array element has no comma: " + condition);
+ throw new DeviceOverridePatternSyntaxError("Array element has no comma", condition);
}
if (posComma == 0) { // condition is empty.
return condition.substring(posComma + 1);
diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
index 7c4156c48..3ea9fedd7 100644
--- a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
+++ b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
@@ -32,6 +32,7 @@ public final class SeekBarDialogPreference extends DialogPreference
public int readValue(final String key);
public int readDefaultValue(final String key);
public void writeValue(final int value, final String key);
+ public void writeDefaultValue(final String key);
public void feedbackValue(final int value);
}
@@ -122,12 +123,16 @@ public final class SeekBarDialogPreference extends DialogPreference
@Override
public void onClick(final DialogInterface dialog, final int which) {
super.onClick(dialog, which);
+ final String key = getKey();
if (which == DialogInterface.BUTTON_NEUTRAL) {
- setValue(clipValue(mValueProxy.readDefaultValue(getKey())), false /* fromUser */);
+ setValue(clipValue(mValueProxy.readDefaultValue(key)), false /* fromUser */);
+ mValueProxy.writeDefaultValue(key);
+ return;
}
- if (which != DialogInterface.BUTTON_NEGATIVE) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
setSummary(mValueView.getText());
- mValueProxy.writeValue(getClippedValueFromProgress(mSeekBar.getProgress()), getKey());
+ mValueProxy.writeValue(getClippedValueFromProgress(mSeekBar.getProgress()), key);
+ return;
}
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index 835ef7b46..7225cd6bf 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -207,6 +207,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment
if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) {
removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen());
+ } else {
+ AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this);
}
setupKeyLongpressTimeoutSettings(prefs, res);
@@ -327,6 +329,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
@Override
+ public void writeDefaultValue(final String key) {
+ sp.edit().remove(key).apply();
+ }
+
+ @Override
public int readValue(final String key) {
return Settings.readKeypressVibrationDuration(sp, res);
}
@@ -357,6 +364,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
@Override
+ public void writeDefaultValue(final String key) {
+ sp.edit().remove(key).apply();
+ }
+
+ @Override
public int readValue(final String key) {
return Settings.readKeyLongpressTimeout(sp, res);
}
@@ -395,6 +407,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
@Override
+ public void writeDefaultValue(final String key) {
+ sp.edit().remove(key).apply();
+ }
+
+ @Override
public int readValue(final String key) {
return getPercentageFromValue(Settings.readKeypressSoundVolume(sp, res));
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 615b2dfab..09102447f 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -80,6 +80,10 @@ public final class SettingsValues {
private final boolean mVoiceKeyEnabled;
private final boolean mVoiceKeyOnMain;
+ // Setting values for additional features
+ public final int[] mAdditionalFeaturesSettingValues =
+ new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+
// Debug settings
public final boolean mIsInternal;
@@ -96,7 +100,7 @@ public final class SettingsValues {
mWordConnectors =
StringUtils.toCodePointArray(res.getString(R.string.symbols_word_connectors));
Arrays.sort(mWordConnectors);
- final String[] suggestPuncsSpec = StringUtils.parseCsvString(res.getString(
+ final String[] suggestPuncsSpec = KeySpecParser.splitKeySpecs(res.getString(
R.string.suggested_punctuations));
mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
mWordSeparators = res.getString(R.string.symbols_word_separators);
@@ -149,6 +153,8 @@ public final class SettingsValues {
Settings.PREF_SHOW_SUGGESTIONS_SETTING,
res.getString(R.string.prefs_suggestion_visibility_default_value));
mSuggestionVisibility = createSuggestionVisibility(res, showSuggestionsSetting);
+ AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray(
+ prefs, mAdditionalFeaturesSettingValues);
mIsInternal = Settings.isInternal(prefs);
}
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index ab050d7a3..c2fd4fb32 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -35,33 +35,55 @@ public final class StringUtils {
return text.codePointCount(0, text.length());
}
- public static boolean containsInArray(final String key, final String[] array) {
+ public static boolean containsInArray(final String text, final String[] array) {
for (final String element : array) {
- if (key.equals(element)) return true;
+ if (text.equals(element)) return true;
}
return false;
}
- public static boolean containsInCsv(final String key, final String csv) {
- if (TextUtils.isEmpty(csv)) return false;
- return containsInArray(key, csv.split(","));
+ /**
+ * Comma-Splittable Text is similar to Comma-Separated Values (CSV) but has much simpler syntax.
+ * Unlike CSV, Comma-Splittable Text has no escaping mechanism, so that the text can't contain
+ * a comma character in it.
+ */
+ private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ",";
+
+ public static boolean containsInCommaSplittableText(final String text,
+ final String extraValues) {
+ if (TextUtils.isEmpty(extraValues)) {
+ return false;
+ }
+ return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT));
}
- public static String appendToCsvIfNotExists(final String key, final String csv) {
- if (TextUtils.isEmpty(csv)) return key;
- if (containsInCsv(key, csv)) return csv;
- return csv + "," + key;
+ public static String appendToCommaSplittableTextIfNotExists(final String text,
+ final String extraValues) {
+ if (TextUtils.isEmpty(extraValues)) {
+ return text;
+ }
+ if (containsInCommaSplittableText(text, extraValues)) {
+ return extraValues;
+ }
+ return extraValues + SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT + text;
}
- public static String removeFromCsvIfExists(final String key, final String csv) {
- if (TextUtils.isEmpty(csv)) return "";
- final String[] elements = csv.split(",");
- if (!containsInArray(key, elements)) return csv;
+ public static String removeFromCommaSplittableTextIfExists(final String text,
+ final String extraValues) {
+ if (TextUtils.isEmpty(extraValues)) {
+ return "";
+ }
+ final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT);
+ if (!containsInArray(text, elements)) {
+ return extraValues;
+ }
final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1);
for (final String element : elements) {
- if (!key.equals(element)) result.add(element);
+ if (!text.equals(element)) {
+ result.add(element);
+ }
}
- return TextUtils.join(",", result);
+ return TextUtils.join(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT, result);
}
/**
@@ -131,44 +153,6 @@ public final class StringUtils {
return codePoints;
}
- public static String[] parseCsvString(final String text) {
- final int size = text.length();
- if (size == 0) {
- return null;
- }
- if (codePointCount(text) == 1) {
- return text.codePointAt(0) == Constants.CSV_SEPARATOR ? null : new String[] { text };
- }
-
- ArrayList<String> list = null;
- int start = 0;
- for (int pos = 0; pos < size; pos++) {
- final char c = text.charAt(pos);
- if (c == Constants.CSV_SEPARATOR) {
- // Skip empty entry.
- if (pos - start > 0) {
- if (list == null) {
- list = CollectionUtils.newArrayList();
- }
- list.add(text.substring(start, pos));
- }
- // Skip comma
- start = pos + 1;
- } else if (c == Constants.CSV_ESCAPE) {
- // Skip escape character and escaped character.
- pos++;
- }
- }
- final String remain = (size - start > 0) ? text.substring(start) : null;
- if (list == null) {
- return remain != null ? new String[] { remain } : null;
- }
- if (remain != null) {
- list.add(remain);
- }
- return list.toArray(new String[list.size()]);
- }
-
// This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE.
public static int getCapitalizationType(final String text) {
// If the first char is not uppercase, then the word is either all lower case or
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index dc9bef22a..5d580f29b 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -229,7 +229,7 @@ public final class Suggest {
// or if it's a 2+ characters non-word (i.e. it's not in the dictionary).
final boolean allowsToBeAutoCorrected = (null != whitelistedWord
&& !whitelistedWord.equals(consideredWord))
- || (consideredWord.length() > 1 && !AutoCorrection.isInTheDictionary(mDictionaries,
+ || (consideredWord.length() > 1 && !AutoCorrection.isValidWord(this,
consideredWord, wordComposer.isFirstCharCapitalized()));
final boolean hasAutoCorrection;
@@ -379,7 +379,8 @@ public final class Suggest {
typedWord, cur.toString(), cur.mScore);
final String scoreInfoString;
if (normalizedScore > 0) {
- scoreInfoString = String.format("%d (%4.2f)", cur.mScore, normalizedScore);
+ scoreInfoString = String.format(
+ Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore);
} else {
scoreInfoString = Integer.toString(cur.mScore);
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index dfddb0ffe..1f453273b 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -132,7 +132,10 @@ public final class SuggestedWords {
public static final int KIND_APP_DEFINED = 6; // Suggested by the application
public static final int KIND_SHORTCUT = 7; // A shortcut
public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
- public static final int KIND_RESUMED = 9; // A resumed suggestion (comes from a span)
+ // KIND_RESUMED: A resumed suggestion (comes from a span, currently this type is used only
+ // in java for re-correction)
+ public static final int KIND_RESUMED = 9;
+ public static final int KIND_OOV_CORRECTION = 10; // Most probable string correction
public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 0f96c54dc..949720fda 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -64,7 +64,8 @@ public final class Utils {
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete.
*/
- public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) {
+ public static void cancelTask(final AsyncTask<?, ?, ?> task,
+ final boolean mayInterruptIfRunning) {
if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
task.cancel(mayInterruptIfRunning);
}
@@ -86,26 +87,34 @@ public final class Utils {
private RingCharBuffer() {
// Intentional empty constructor for singleton.
}
+
@UsedForTesting
public static RingCharBuffer getInstance() {
return sRingCharBuffer;
}
- public static RingCharBuffer init(InputMethodService context, boolean enabled,
- boolean usabilityStudy) {
- if (!(enabled || usabilityStudy)) return null;
+
+ public static RingCharBuffer init(final InputMethodService context, final boolean enabled,
+ final boolean usabilityStudy) {
+ if (!(enabled || usabilityStudy)) {
+ return null;
+ }
sRingCharBuffer.mContext = context;
sRingCharBuffer.mEnabled = true;
UsabilityStudyLogUtils.getInstance().init(context);
return sRingCharBuffer;
}
- private static int normalize(int in) {
+
+ private static int normalize(final int in) {
int ret = in % BUFSIZE;
return ret < 0 ? ret + BUFSIZE : ret;
}
+
// TODO: accept code points
@UsedForTesting
- public void push(char c, int x, int y) {
- if (!mEnabled) return;
+ public void push(final char c, final int x, final int y) {
+ if (!mEnabled) {
+ return;
+ }
mCharBuf[mEnd] = c;
mXBuf[mEnd] = x;
mYBuf[mEnd] = y;
@@ -114,52 +123,54 @@ public final class Utils {
++mLength;
}
}
+
public char pop() {
if (mLength < 1) {
return PLACEHOLDER_DELIMITER_CHAR;
- } else {
- mEnd = normalize(mEnd - 1);
- --mLength;
- return mCharBuf[mEnd];
}
+ mEnd = normalize(mEnd - 1);
+ --mLength;
+ return mCharBuf[mEnd];
}
- public char getBackwardNthChar(int n) {
+
+ public char getBackwardNthChar(final int n) {
if (mLength <= n || n < 0) {
return PLACEHOLDER_DELIMITER_CHAR;
- } else {
- return mCharBuf[normalize(mEnd - n - 1)];
}
+ return mCharBuf[normalize(mEnd - n - 1)];
}
- public int getPreviousX(char c, int back) {
- int index = normalize(mEnd - 2 - back);
+
+ public int getPreviousX(final char c, final int back) {
+ final int index = normalize(mEnd - 2 - back);
if (mLength <= back
|| Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) {
return INVALID_COORDINATE;
- } else {
- return mXBuf[index];
}
+ return mXBuf[index];
}
- public int getPreviousY(char c, int back) {
+
+ public int getPreviousY(final char c, final int back) {
int index = normalize(mEnd - 2 - back);
if (mLength <= back
|| Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) {
return INVALID_COORDINATE;
- } else {
- return mYBuf[index];
}
+ return mYBuf[index];
}
- public String getLastWord(int ignoreCharCount) {
- StringBuilder sb = new StringBuilder();
+
+ public String getLastWord(final int ignoreCharCount) {
+ final StringBuilder sb = new StringBuilder();
+ final LatinIME latinIme = (LatinIME)mContext;
int i = ignoreCharCount;
for (; i < mLength; ++i) {
- char c = mCharBuf[normalize(mEnd - 1 - i)];
- if (!((LatinIME)mContext).isWordSeparator(c)) {
+ final char c = mCharBuf[normalize(mEnd - 1 - i)];
+ if (!latinIme.isWordSeparator(c)) {
break;
}
}
for (; i < mLength; ++i) {
char c = mCharBuf[normalize(mEnd - 1 - i)];
- if (!((LatinIME)mContext).isWordSeparator(c)) {
+ if (!latinIme.isWordSeparator(c)) {
sb.append(c);
} else {
break;
@@ -167,6 +178,7 @@ public final class Utils {
}
return sb.reverse().toString();
}
+
public void reset() {
mLength = 0;
}
@@ -174,11 +186,11 @@ public final class Utils {
// Get the current stack trace
public static String getStackTrace(final int limit) {
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
try {
throw new RuntimeException();
- } catch (RuntimeException e) {
- StackTraceElement[] frames = e.getStackTrace();
+ } catch (final RuntimeException e) {
+ final StackTraceElement[] frames = e.getStackTrace();
// Start at 1 because the first frame is here and we don't care about it
for (int j = 1; j < frames.length && j < limit + 1; ++j) {
sb.append(frames[j].toString() + "\n");
@@ -222,7 +234,7 @@ public final class Utils {
return OnDemandInitializationHolder.sInstance;
}
- public void init(InputMethodService ims) {
+ public void init(final InputMethodService ims) {
mIms = ims;
mDirectory = ims.getFilesDir();
}
@@ -232,17 +244,17 @@ public final class Utils {
&& (mDirectory != null && mDirectory.exists())) {
try {
mWriter = getPrintWriter(mDirectory, FILENAME, false);
- } catch (IOException e) {
+ } catch (final IOException e) {
Log.e(USABILITY_TAG, "Can't create log file.");
}
}
}
- public static void writeBackSpace(int x, int y) {
+ public static void writeBackSpace(final int x, final int y) {
UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y);
}
- public void writeChar(char c, int x, int y) {
+ public static void writeChar(final char c, final int x, final int y) {
String inputChar = String.valueOf(c);
switch (c) {
case '\n':
@@ -279,15 +291,15 @@ public final class Utils {
private synchronized String getBufferedLogs() {
mWriter.flush();
- StringBuilder sb = new StringBuilder();
- BufferedReader br = getBufferedReader();
+ final StringBuilder sb = new StringBuilder();
+ final BufferedReader br = getBufferedReader();
String line;
try {
while ((line = br.readLine()) != null) {
sb.append('\n');
sb.append(line);
}
- } catch (IOException e) {
+ } catch (final IOException e) {
Log.e(USABILITY_TAG, "Can't read log file.");
} finally {
if (LatinImeLogger.sDBG) {
@@ -295,7 +307,7 @@ public final class Utils {
}
try {
br.close();
- } catch (IOException e) {
+ } catch (final IOException e) {
// ignore.
}
}
@@ -334,10 +346,10 @@ public final class Utils {
srcStream.close();
dest.close();
destStream.close();
- } catch (FileNotFoundException e1) {
+ } catch (final FileNotFoundException e1) {
Log.w(USABILITY_TAG, e1);
return;
- } catch (IOException e2) {
+ } catch (final IOException e2) {
Log.w(USABILITY_TAG, e2);
return;
}
@@ -387,13 +399,13 @@ public final class Utils {
createLogFileIfNotExist();
try {
return new BufferedReader(new FileReader(mFile));
- } catch (FileNotFoundException e) {
+ } catch (final FileNotFoundException e) {
return null;
}
}
- private PrintWriter getPrintWriter(
- File dir, String filename, boolean renew) throws IOException {
+ private PrintWriter getPrintWriter(final File dir, final String filename,
+ final boolean renew) throws IOException {
mFile = new File(dir, filename);
if (mFile.exists()) {
if (renew) {
@@ -405,8 +417,7 @@ public final class Utils {
}
public static final class Stats {
- public static void onNonSeparator(final char code, final int x,
- final int y) {
+ public static void onNonSeparator(final char code, final int x, final int y) {
RingCharBuffer.getInstance().push(code, x, y);
LatinImeLogger.logOnInputChar();
}
@@ -430,7 +441,9 @@ public final class Utils {
public static void onAutoCorrection(final String typedWord, final String correctedWord,
final String separatorString, final WordComposer wordComposer) {
final boolean isBatchMode = wordComposer.isBatchMode();
- if (!isBatchMode && TextUtils.isEmpty(typedWord)) return;
+ if (!isBatchMode && TextUtils.isEmpty(typedWord)) {
+ return;
+ }
// TODO: this fails when the separator is more than 1 code point long, but
// the backend can't handle it yet. The only case when this happens is with
// smileys and other multi-character keys.
@@ -454,36 +467,43 @@ public final class Utils {
}
public static String getDebugInfo(final SuggestedWords suggestions, final int pos) {
- if (!LatinImeLogger.sDBG) return null;
+ if (!LatinImeLogger.sDBG) {
+ return null;
+ }
final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
- if (wordInfo == null) return null;
+ if (wordInfo == null) {
+ return null;
+ }
final String info = wordInfo.getDebugString();
- if (TextUtils.isEmpty(info)) return null;
+ if (TextUtils.isEmpty(info)) {
+ return null;
+ }
return info;
}
- public static int getAcitivityTitleResId(Context context, Class<? extends Activity> cls) {
+ public static int getAcitivityTitleResId(final Context context,
+ final Class<? extends Activity> cls) {
final ComponentName cn = new ComponentName(context, cls);
try {
final ActivityInfo ai = context.getPackageManager().getActivityInfo(cn, 0);
if (ai != null) {
return ai.labelRes;
}
- } catch (NameNotFoundException e) {
+ } catch (final NameNotFoundException e) {
Log.e(TAG, "Failed to get settings activity title res id.", e);
}
return 0;
}
- public static String getVersionName(Context context) {
+ public static String getVersionName(final Context context) {
try {
if (context == null) {
return "";
}
final String packageName = context.getPackageName();
- PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
+ final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
return info.versionName;
- } catch (NameNotFoundException e) {
+ } catch (final NameNotFoundException e) {
Log.e(TAG, "Could not find version info.", e);
}
return "";
diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
index 93687e193..a446672cb 100644
--- a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
+++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
@@ -23,6 +23,7 @@ import android.util.Patterns;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
public class AccountUtils {
private AccountUtils() {
@@ -44,4 +45,22 @@ public class AccountUtils {
}
return retval;
}
+
+ /**
+ * Get all device accounts having specified domain name.
+ * @param context application context
+ * @param domain domain name used for filtering
+ * @return List of account names that contain the specified domain name
+ */
+ public static List<String> getDeviceAccountsWithDomain(
+ final Context context, final String domain) {
+ final ArrayList<String> retval = new ArrayList<String>();
+ final String atDomain = "@" + domain.toLowerCase(Locale.ROOT);
+ for (final Account account : getAccounts(context)) {
+ if (account.name.toLowerCase(Locale.ROOT).endsWith(atDomain)) {
+ retval.add(account.name);
+ }
+ }
+ return retval;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java
index 6a7cd9b6f..604ebeeb6 100644
--- a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java
+++ b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java
@@ -91,7 +91,7 @@ public final class LauncherIconVisibilityManager extends BroadcastReceiver {
} else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
Log.i(TAG, "Boot has been completed");
return true;
- } else if (IntentCompatUtils.has_ACTION_USER_INITIALIZE(intent)) {
+ } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(action)) {
Log.i(TAG, "User initialize");
return true;
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
new file mode 100644
index 000000000..d9bb3eaf4
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.suggestions;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.CharacterStyle;
+import android.text.style.StyleSpan;
+import android.text.style.UnderlineSpan;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.inputmethod.keyboard.ViewLayoutUtils;
+import com.android.inputmethod.latin.AutoCorrection;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResourceUtils;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.Utils;
+
+import java.util.ArrayList;
+
+final class SuggestionStripLayoutHelper {
+ private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
+ private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
+ private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
+ private static final int PUNCTUATIONS_IN_STRIP = 5;
+ private static final float MIN_TEXT_XSCALE = 0.70f;
+
+ public final int mPadding;
+ public final int mDividerWidth;
+ public final int mSuggestionsStripHeight;
+ public final int mSuggestionsCountInStrip;
+ public final int mMoreSuggestionsRowHeight;
+ private int mMaxMoreSuggestionsRow;
+ public final float mMinMoreSuggestionsWidth;
+ public final int mMoreSuggestionsBottomGap;
+
+ private final ArrayList<TextView> mWordViews;
+ private final ArrayList<View> mDividerViews;
+ private final ArrayList<TextView> mDebugInfoViews;
+
+ private final int mColorValidTypedWord;
+ private final int mColorTypedWord;
+ private final int mColorAutoCorrect;
+ private final int mColorSuggested;
+ private final float mAlphaObsoleted;
+ private final float mCenterSuggestionWeight;
+ private final int mCenterPositionInStrip;
+ private final Drawable mMoreSuggestionsHint;
+ private static final String MORE_SUGGESTIONS_HINT = "\u2026";
+ private static final String LEFTWARDS_ARROW = "\u2190";
+
+ private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
+ private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
+ private static final int AUTO_CORRECT_BOLD = 0x01;
+ private static final int AUTO_CORRECT_UNDERLINE = 0x02;
+ private static final int VALID_TYPED_WORD_BOLD = 0x04;
+
+ private final int mSuggestionStripOption;
+
+ private final ArrayList<CharSequence> mWords = CollectionUtils.newArrayList();
+
+ public boolean mMoreSuggestionsAvailable;
+
+ private final TextView mWordToSaveView;
+ private final TextView mLeftwardsArrowView;
+ private final TextView mHintToSaveView;
+
+ public SuggestionStripLayoutHelper(final Context context, final AttributeSet attrs,
+ final int defStyle, final ArrayList<TextView> wordViews,
+ final ArrayList<View> dividerViews, final ArrayList<TextView> debugInfoViews) {
+ mWordViews = wordViews;
+ mDividerViews = dividerViews;
+ mDebugInfoViews = debugInfoViews;
+
+ final TextView wordView = wordViews.get(0);
+ final View dividerView = dividerViews.get(0);
+ mPadding = wordView.getCompoundPaddingLeft() + wordView.getCompoundPaddingRight();
+ dividerView.measure(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mDividerWidth = dividerView.getMeasuredWidth();
+
+ final Resources res = wordView.getResources();
+ mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
+ mSuggestionStripOption = a.getInt(
+ R.styleable.SuggestionStripView_suggestionStripOption, 0);
+ final float alphaValidTypedWord = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f);
+ final float alphaTypedWord = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaTypedWord, 1.0f);
+ final float alphaAutoCorrect = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f);
+ final float alphaSuggested = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
+ mAlphaObsoleted = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
+ mColorValidTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
+ mColorTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
+ mColorAutoCorrect = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
+ mColorSuggested = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
+ mSuggestionsCountInStrip = a.getInt(
+ R.styleable.SuggestionStripView_suggestionsCountInStrip,
+ DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
+ mCenterSuggestionWeight = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_centerSuggestionPercentile,
+ DEFAULT_CENTER_SUGGESTION_PERCENTILE);
+ mMaxMoreSuggestionsRow = a.getInt(
+ R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
+ DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
+ mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f);
+ a.recycle();
+
+ mMoreSuggestionsHint = getMoreSuggestionsHint(res,
+ res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
+ mCenterPositionInStrip = mSuggestionsCountInStrip / 2;
+ mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
+ R.dimen.more_suggestions_bottom_gap);
+ mMoreSuggestionsRowHeight = res.getDimensionPixelSize(R.dimen.more_suggestions_row_height);
+
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
+ mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
+ mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
+ }
+
+ public int getMaxMoreSuggestionsRow() {
+ return mMaxMoreSuggestionsRow;
+ }
+
+ private int getMoreSuggestionsHeight() {
+ return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
+ }
+
+ public int setMoreSuggestionsHeight(final int remainingHeight) {
+ final int currentHeight = getMoreSuggestionsHeight();
+ if (currentHeight <= remainingHeight) {
+ return currentHeight;
+ }
+
+ mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap)
+ / mMoreSuggestionsRowHeight;
+ final int newHeight = getMoreSuggestionsHeight();
+ return newHeight;
+ }
+
+ private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
+ final int color) {
+ final Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setTextAlign(Align.CENTER);
+ paint.setTextSize(textSize);
+ paint.setColor(color);
+ final Rect bounds = new Rect();
+ paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds);
+ final int width = Math.round(bounds.width() + 0.5f);
+ final int height = Math.round(bounds.height() + 0.5f);
+ final Bitmap buffer = Bitmap.createBitmap(width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(buffer);
+ canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
+ return new BitmapDrawable(res, buffer);
+ }
+
+ private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
+ final int indexInSuggestedWords) {
+ final String word = suggestedWords.getWord(indexInSuggestedWords);
+ final boolean isAutoCorrect = indexInSuggestedWords == 1
+ && suggestedWords.willAutoCorrect();
+ final boolean isTypedWordValid = indexInSuggestedWords == 0
+ && suggestedWords.mTypedWordValid;
+ if (!isAutoCorrect && !isTypedWordValid) {
+ return word;
+ }
+
+ final int len = word.length();
+ final Spannable spannedWord = new SpannableString(word);
+ final int option = mSuggestionStripOption;
+ if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0)
+ || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) {
+ spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ }
+ if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) {
+ spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ }
+ return spannedWord;
+ }
+
+ private int getIndexInSuggestedWords(final int positionInStrip,
+ final SuggestedWords suggestedWords) {
+ // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
+ // suggestions.
+ final int mostImportantIndexInSuggestedWords = suggestedWords.willAutoCorrect() ? 1 : 0;
+ if (positionInStrip == mCenterPositionInStrip) {
+ return mostImportantIndexInSuggestedWords;
+ }
+ if (positionInStrip == mostImportantIndexInSuggestedWords) {
+ return mCenterPositionInStrip;
+ }
+ return positionInStrip;
+ }
+
+ private int getSuggestionTextColor(final int positionInStrip,
+ final SuggestedWords suggestedWords) {
+ final int indexInSuggestedWords = getIndexInSuggestedWords(
+ positionInStrip, suggestedWords);
+ // TODO: Need to revisit this logic with bigram suggestions
+ final boolean isSuggested = (indexInSuggestedWords != 0);
+
+ final int color;
+ if (positionInStrip == mCenterPositionInStrip && suggestedWords.willAutoCorrect()) {
+ color = mColorAutoCorrect;
+ } else if (positionInStrip == mCenterPositionInStrip && suggestedWords.mTypedWordValid) {
+ color = mColorValidTypedWord;
+ } else if (isSuggested) {
+ color = mColorSuggested;
+ } else {
+ color = mColorTypedWord;
+ }
+ if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
+ // If we auto-correct, then the autocorrection is in slot 0 and the typed word
+ // is in slot 1.
+ if (positionInStrip == mCenterPositionInStrip
+ && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
+ suggestedWords.getWord(1), suggestedWords.getWord(0))) {
+ return 0xFFFF0000;
+ }
+ }
+
+ if (suggestedWords.mIsObsoleteSuggestions && isSuggested) {
+ return applyAlpha(color, mAlphaObsoleted);
+ }
+ return color;
+ }
+
+ private static int applyAlpha(final int color, final float alpha) {
+ final int newAlpha = (int)(Color.alpha(color) * alpha);
+ return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ private static void addDivider(final ViewGroup stripView, final View dividerView) {
+ stripView.addView(dividerView);
+ final LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams)dividerView.getLayoutParams();
+ params.gravity = Gravity.CENTER;
+ }
+
+ public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
+ final ViewGroup placerView) {
+ if (suggestedWords.mIsPunctuationSuggestions) {
+ layoutPunctuationSuggestions(suggestedWords, stripView);
+ return;
+ }
+
+ final int countInStrip = mSuggestionsCountInStrip;
+ setupWords(suggestedWords, countInStrip);
+ mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
+ int x = 0;
+ for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) {
+ if (positionInStrip != 0) {
+ final View divider = mDividerViews.get(positionInStrip);
+ // Add divider if this isn't the left most suggestion in suggestions strip.
+ addDivider(stripView, divider);
+ x += divider.getMeasuredWidth();
+ }
+
+ final int width = getSuggestionWidth(positionInStrip, placerView.getWidth());
+ final TextView wordView = layoutWord(suggestedWords, positionInStrip, width);
+ stripView.addView(wordView);
+ setLayoutWeight(wordView, getSuggestionWeight(positionInStrip),
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ x += wordView.getMeasuredWidth();
+
+ if (SuggestionStripView.DBG) {
+ layoutDebugInfo(suggestedWords, positionInStrip, placerView, x);
+ }
+ }
+ }
+
+ /**
+ * Format appropriately the suggested word indirectly specified by
+ * <code>positionInStrip</code> as text in a corresponding {@link TextView}. When the
+ * suggested word doesn't exist, the corresponding {@link TextView} will be disabled
+ * and never respond to user interaction. The suggested word may be shrunk or ellipsized to
+ * fit in the specified width.
+ *
+ * The <code>positionInStrip</code> argument is the index in the suggestion strip. The indices
+ * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0.
+ * The index of the most important suggestion is in {@link #mCenterPositionInStrip}. This
+ * usually doesn't match the index in <code>suggedtedWords</code> -- see
+ * {@link #getIndexInSuggestedWords(int,SuggestedWords)}.
+ *
+ * @param suggestedWords the list of suggestions.
+ * @param positionInStrip the in the suggestion strip.
+ * @param width the maximum width for layout in pixels.
+ * @return the {@link TextView} containing the suggested word appropriately formatted.
+ */
+ private TextView layoutWord(final SuggestedWords suggestedWords, final int positionInStrip,
+ final int width) {
+ final int indexInSuggestedWords = getIndexInSuggestedWords(positionInStrip, suggestedWords);
+ final CharSequence word = mWords.get(indexInSuggestedWords);
+ final TextView wordView = mWordViews.get(indexInSuggestedWords);
+ if (positionInStrip == mCenterPositionInStrip && mMoreSuggestionsAvailable) {
+ // TODO: This "more suggestions hint" should have a nicely designed icon.
+ wordView.setCompoundDrawablesWithIntrinsicBounds(
+ null, null, null, mMoreSuggestionsHint);
+ // HACK: Align with other TextViews that have no compound drawables.
+ wordView.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
+ } else {
+ wordView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+ }
+
+ // Disable this suggestion if the suggestion is null or empty.
+ wordView.setEnabled(!TextUtils.isEmpty(word));
+ wordView.setTextColor(getSuggestionTextColor(positionInStrip, suggestedWords));
+ final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
+ final float scaleX = wordView.getTextScaleX();
+ wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
+ wordView.setTextScaleX(scaleX);
+ return wordView;
+ }
+
+ private void layoutDebugInfo(final SuggestedWords suggestedWords, final int positionInStrip,
+ final ViewGroup placerView, final int x) {
+ final int indexInSuggestedWords = getIndexInSuggestedWords(positionInStrip, suggestedWords);
+ if (indexInSuggestedWords >= suggestedWords.size()) {
+ return;
+ }
+ final String debugInfo = Utils.getDebugInfo(suggestedWords, indexInSuggestedWords);
+ if (debugInfo == null) {
+ return;
+ }
+ final TextView debugInfoView = mDebugInfoViews.get(indexInSuggestedWords);
+ debugInfoView.setText(debugInfo);
+ placerView.addView(debugInfoView);
+ debugInfoView.measure(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ final int infoWidth = debugInfoView.getMeasuredWidth();
+ final int y = debugInfoView.getMeasuredHeight();
+ ViewLayoutUtils.placeViewAt(
+ debugInfoView, x - infoWidth, y, infoWidth, debugInfoView.getMeasuredHeight());
+ }
+
+ private int getSuggestionWidth(final int positionInStrip, final int maxWidth) {
+ final int paddings = mPadding * mSuggestionsCountInStrip;
+ final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
+ final int availableWidth = maxWidth - paddings - dividers;
+ return (int)(availableWidth * getSuggestionWeight(positionInStrip));
+ }
+
+ private float getSuggestionWeight(final int positionInStrip) {
+ if (positionInStrip == mCenterPositionInStrip) {
+ return mCenterSuggestionWeight;
+ }
+ // TODO: Revisit this for cases of 5 or more suggestions
+ return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1);
+ }
+
+ private void setupWords(final SuggestedWords suggestedWords, final int countInStrip) {
+ mWords.clear();
+ final int count = Math.min(suggestedWords.size(), countInStrip);
+ for (int pos = 0; pos < count; pos++) {
+ final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos);
+ mWords.add(styled);
+ }
+ for (int pos = count; pos < countInStrip; pos++) {
+ // Make this inactive for touches in layout().
+ mWords.add(null);
+ }
+ }
+
+ private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
+ final ViewGroup stripView) {
+ final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
+ for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) {
+ if (indexInStrip != 0) {
+ // Add divider if this isn't the left most suggestion in suggestions strip.
+ addDivider(stripView, mDividerViews.get(indexInStrip));
+ }
+
+ final TextView word = mWordViews.get(indexInStrip);
+ word.setEnabled(true);
+ word.setTextColor(mColorAutoCorrect);
+ final String text = suggestedWords.getWord(indexInStrip);
+ word.setText(text);
+ word.setTextScaleX(1.0f);
+ word.setCompoundDrawables(null, null, null, null);
+ stripView.addView(word);
+ setLayoutWeight(word, 1.0f, mSuggestionsStripHeight);
+ }
+ mMoreSuggestionsAvailable = false;
+ }
+
+ public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView,
+ final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
+ final int width = stripWidth - mDividerWidth - mPadding * 2;
+
+ final TextView wordView = mWordToSaveView;
+ wordView.setTextColor(mColorTypedWord);
+ final int wordWidth = (int)(width * mCenterSuggestionWeight);
+ final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint());
+ final float wordScaleX = wordView.getTextScaleX();
+ wordView.setTag(word);
+ wordView.setText(text);
+ wordView.setTextScaleX(wordScaleX);
+ stripView.addView(wordView);
+ setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
+
+ stripView.addView(mDividerViews.get(0));
+
+ final TextView leftArrowView = mLeftwardsArrowView;
+ leftArrowView.setTextColor(mColorAutoCorrect);
+ leftArrowView.setText(LEFTWARDS_ARROW);
+ stripView.addView(leftArrowView);
+
+ final TextView hintView = mHintToSaveView;
+ hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ hintView.setTextColor(mColorAutoCorrect);
+ final int hintWidth = width - wordWidth - leftArrowView.getWidth();
+ final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
+ hintView.setText(hintText);
+ hintView.setTextScaleX(hintScaleX);
+ stripView.addView(hintView);
+ setLayoutWeight(
+ hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
+
+ wordView.setOnClickListener(listener);
+ leftArrowView.setOnClickListener(listener);
+ hintView.setOnClickListener(listener);
+ }
+
+ public CharSequence getAddToDictionaryWord() {
+ return (CharSequence)mWordToSaveView.getTag();
+ }
+
+ public boolean isAddToDictionaryShowing(final View v) {
+ return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
+ }
+
+ private static void setLayoutWeight(final View v, final float weight, final int height) {
+ final ViewGroup.LayoutParams lp = v.getLayoutParams();
+ if (lp instanceof LinearLayout.LayoutParams) {
+ final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
+ llp.weight = weight;
+ llp.width = 0;
+ llp.height = height;
+ }
+ }
+
+ private static float getTextScaleX(final CharSequence text, final int maxWidth,
+ final TextPaint paint) {
+ paint.setTextScaleX(1.0f);
+ final int width = getTextWidth(text, paint);
+ if (width <= maxWidth) {
+ return 1.0f;
+ }
+ return maxWidth / (float)width;
+ }
+
+ private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
+ final TextPaint paint) {
+ if (text == null) {
+ return null;
+ }
+ final float scaleX = getTextScaleX(text, maxWidth, paint);
+ if (scaleX >= MIN_TEXT_XSCALE) {
+ paint.setTextScaleX(scaleX);
+ return text;
+ }
+
+ // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
+ // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
+ final CharSequence ellipsized = TextUtils.ellipsize(
+ text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
+ paint.setTextScaleX(MIN_TEXT_XSCALE);
+ return ellipsized;
+ }
+
+ private static int getTextWidth(final CharSequence text, final TextPaint paint) {
+ if (TextUtils.isEmpty(text)) {
+ return 0;
+ }
+ final Typeface savedTypeface = paint.getTypeface();
+ paint.setTypeface(getTextTypeface(text));
+ final int len = text.length();
+ final float[] widths = new float[len];
+ final int count = paint.getTextWidths(text, 0, len, widths);
+ int width = 0;
+ for (int i = 0; i < count; i++) {
+ width += Math.round(widths[i] + 0.5f);
+ }
+ paint.setTypeface(savedTypeface);
+ return width;
+ }
+
+ private static Typeface getTextTypeface(final CharSequence text) {
+ if (!(text instanceof SpannableString)) {
+ return Typeface.DEFAULT;
+ }
+
+ final SpannableString ss = (SpannableString)text;
+ final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
+ if (styles.length == 0) {
+ return Typeface.DEFAULT;
+ }
+
+ if (styles[0].getStyle() == Typeface.BOLD) {
+ return Typeface.DEFAULT_BOLD;
+ }
+ // TODO: BOLD_ITALIC, ITALIC case?
+ return Typeface.DEFAULT;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index ad350a02f..226bf873e 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -18,34 +18,14 @@ package com.android.inputmethod.latin.suggestions;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.style.CharacterStyle;
-import android.text.style.StyleSpan;
-import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.view.GestureDetector;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -53,16 +33,13 @@ import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
-import com.android.inputmethod.keyboard.ViewLayoutUtils;
-import com.android.inputmethod.latin.AutoCorrection;
+import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
import com.android.inputmethod.research.ResearchLogger;
@@ -88,477 +65,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final MoreSuggestionsView mMoreSuggestionsView;
private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
- private final ArrayList<TextView> mWords = CollectionUtils.newArrayList();
- private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList();
- private final ArrayList<View> mDividers = CollectionUtils.newArrayList();
+ private final ArrayList<TextView> mWordViews = CollectionUtils.newArrayList();
+ private final ArrayList<TextView> mDebugInfoViews = CollectionUtils.newArrayList();
+ private final ArrayList<View> mDividerViews = CollectionUtils.newArrayList();
Listener mListener;
private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
- private final SuggestionStripViewParams mParams;
- private static final float MIN_TEXT_XSCALE = 0.70f;
-
- private static final class SuggestionStripViewParams {
- private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
- private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
- private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
- private static final int PUNCTUATIONS_IN_STRIP = 5;
-
- public final int mPadding;
- public final int mDividerWidth;
- public final int mSuggestionsStripHeight;
- public final int mSuggestionsCountInStrip;
- public final int mMoreSuggestionsRowHeight;
- private int mMaxMoreSuggestionsRow;
- public final float mMinMoreSuggestionsWidth;
- public final int mMoreSuggestionsBottomGap;
-
- private final ArrayList<TextView> mWords;
- private final ArrayList<View> mDividers;
- private final ArrayList<TextView> mInfos;
-
- private final int mColorValidTypedWord;
- private final int mColorTypedWord;
- private final int mColorAutoCorrect;
- private final int mColorSuggested;
- private final float mAlphaObsoleted;
- private final float mCenterSuggestionWeight;
- private final int mCenterSuggestionIndex;
- private final Drawable mMoreSuggestionsHint;
- private static final String MORE_SUGGESTIONS_HINT = "\u2026";
- private static final String LEFTWARDS_ARROW = "\u2190";
-
- private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
- private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
- private static final int AUTO_CORRECT_BOLD = 0x01;
- private static final int AUTO_CORRECT_UNDERLINE = 0x02;
- private static final int VALID_TYPED_WORD_BOLD = 0x04;
-
- private final int mSuggestionStripOption;
-
- private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList();
-
- public boolean mMoreSuggestionsAvailable;
-
- private final TextView mWordToSaveView;
- private final TextView mLeftwardsArrowView;
- private final TextView mHintToSaveView;
-
- public SuggestionStripViewParams(final Context context, final AttributeSet attrs,
- final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers,
- final ArrayList<TextView> infos) {
- mWords = words;
- mDividers = dividers;
- mInfos = infos;
-
- final TextView word = words.get(0);
- final View divider = dividers.get(0);
- mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight();
- divider.measure(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- mDividerWidth = divider.getMeasuredWidth();
-
- final Resources res = word.getResources();
- mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
- mSuggestionStripOption = a.getInt(
- R.styleable.SuggestionStripView_suggestionStripOption, 0);
- final float alphaValidTypedWord = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f);
- final float alphaTypedWord = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_alphaTypedWord, 1.0f);
- final float alphaAutoCorrect = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f);
- final float alphaSuggested = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
- mAlphaObsoleted = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
- mColorValidTypedWord = applyAlpha(a.getColor(
- R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
- mColorTypedWord = applyAlpha(a.getColor(
- R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
- mColorAutoCorrect = applyAlpha(a.getColor(
- R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
- mColorSuggested = applyAlpha(a.getColor(
- R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
- mSuggestionsCountInStrip = a.getInt(
- R.styleable.SuggestionStripView_suggestionsCountInStrip,
- DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
- mCenterSuggestionWeight = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_centerSuggestionPercentile,
- DEFAULT_CENTER_SUGGESTION_PERCENTILE);
- mMaxMoreSuggestionsRow = a.getInt(
- R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
- DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
- mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a,
- R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f);
- a.recycle();
-
- mMoreSuggestionsHint = getMoreSuggestionsHint(res,
- res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
- mCenterSuggestionIndex = mSuggestionsCountInStrip / 2;
- mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
- R.dimen.more_suggestions_bottom_gap);
- mMoreSuggestionsRowHeight = res.getDimensionPixelSize(
- R.dimen.more_suggestions_row_height);
-
- final LayoutInflater inflater = LayoutInflater.from(context);
- mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
- mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
- mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null);
- }
-
- public int getMaxMoreSuggestionsRow() {
- return mMaxMoreSuggestionsRow;
- }
-
- private int getMoreSuggestionsHeight() {
- return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
- }
-
- public int setMoreSuggestionsHeight(final int remainingHeight) {
- final int currentHeight = getMoreSuggestionsHeight();
- if (currentHeight <= remainingHeight) {
- return currentHeight;
- }
-
- mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap)
- / mMoreSuggestionsRowHeight;
- final int newHeight = getMoreSuggestionsHeight();
- return newHeight;
- }
-
- private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
- final int color) {
- final Paint paint = new Paint();
- paint.setAntiAlias(true);
- paint.setTextAlign(Align.CENTER);
- paint.setTextSize(textSize);
- paint.setColor(color);
- final Rect bounds = new Rect();
- paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds);
- final int width = Math.round(bounds.width() + 0.5f);
- final int height = Math.round(bounds.height() + 0.5f);
- final Bitmap buffer = Bitmap.createBitmap(
- width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(buffer);
- canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
- return new BitmapDrawable(res, buffer);
- }
-
- private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
- final int pos) {
- final String word = suggestedWords.getWord(pos);
- final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
- final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
- if (!isAutoCorrect && !isTypedWordValid)
- return word;
-
- final int len = word.length();
- final Spannable spannedWord = new SpannableString(word);
- final int option = mSuggestionStripOption;
- if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0)
- || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) {
- spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- }
- if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) {
- spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- }
- return spannedWord;
- }
-
- private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
- // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
- // suggestions.
- final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
- if (index == mCenterSuggestionIndex) {
- return centerPos;
- } else if (index == centerPos) {
- return mCenterSuggestionIndex;
- } else {
- return index;
- }
- }
-
- private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
- final int pos) {
- // TODO: Need to revisit this logic with bigram suggestions
- final boolean isSuggested = (pos != 0);
-
- final int color;
- if (index == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) {
- color = mColorAutoCorrect;
- } else if (index == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) {
- color = mColorValidTypedWord;
- } else if (isSuggested) {
- color = mColorSuggested;
- } else {
- color = mColorTypedWord;
- }
- if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
- // If we auto-correct, then the autocorrection is in slot 0 and the typed word
- // is in slot 1.
- if (index == mCenterSuggestionIndex
- && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
- suggestedWords.getWord(1), suggestedWords.getWord(0))) {
- return 0xFFFF0000;
- }
- }
-
- if (suggestedWords.mIsObsoleteSuggestions && isSuggested) {
- return applyAlpha(color, mAlphaObsoleted);
- } else {
- return color;
- }
- }
-
- private static int applyAlpha(final int color, final float alpha) {
- final int newAlpha = (int)(Color.alpha(color) * alpha);
- return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
- }
-
- private static void addDivider(final ViewGroup stripView, final View divider) {
- stripView.addView(divider);
- final LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams)divider.getLayoutParams();
- params.gravity = Gravity.CENTER;
- }
-
- public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
- final ViewGroup placer, final int stripWidth) {
- if (suggestedWords.mIsPunctuationSuggestions) {
- layoutPunctuationSuggestions(suggestedWords, stripView);
- return;
- }
-
- final int countInStrip = mSuggestionsCountInStrip;
- setupTexts(suggestedWords, countInStrip);
- mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
- int x = 0;
- for (int index = 0; index < countInStrip; index++) {
- final int pos = getWordPosition(index, suggestedWords);
-
- if (index != 0) {
- final View divider = mDividers.get(pos);
- // Add divider if this isn't the left most suggestion in suggestions strip.
- addDivider(stripView, divider);
- x += divider.getMeasuredWidth();
- }
-
- final CharSequence styled = mTexts.get(pos);
- final TextView word = mWords.get(pos);
- if (index == mCenterSuggestionIndex && mMoreSuggestionsAvailable) {
- // TODO: This "more suggestions hint" should have nicely designed icon.
- word.setCompoundDrawablesWithIntrinsicBounds(
- null, null, null, mMoreSuggestionsHint);
- // HACK: To align with other TextView that has no compound drawables.
- word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
- } else {
- word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
- }
-
- // Disable this suggestion if the suggestion is null or empty.
- word.setEnabled(!TextUtils.isEmpty(styled));
- word.setTextColor(getSuggestionTextColor(index, suggestedWords, pos));
- final int width = getSuggestionWidth(index, stripWidth);
- final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
- final float scaleX = word.getTextScaleX();
- word.setText(text); // TextView.setText() resets text scale x to 1.0.
- word.setTextScaleX(scaleX);
- stripView.addView(word);
- setLayoutWeight(
- word, getSuggestionWeight(index), ViewGroup.LayoutParams.MATCH_PARENT);
- x += word.getMeasuredWidth();
-
- if (DBG && pos < suggestedWords.size()) {
- final String debugInfo = Utils.getDebugInfo(suggestedWords, pos);
- if (debugInfo != null) {
- final TextView info = mInfos.get(pos);
- info.setText(debugInfo);
- placer.addView(info);
- info.measure(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- final int infoWidth = info.getMeasuredWidth();
- final int y = info.getMeasuredHeight();
- ViewLayoutUtils.placeViewAt(
- info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
- }
- }
- }
- }
-
- private int getSuggestionWidth(final int index, final int maxWidth) {
- final int paddings = mPadding * mSuggestionsCountInStrip;
- final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
- final int availableWidth = maxWidth - paddings - dividers;
- return (int)(availableWidth * getSuggestionWeight(index));
- }
-
- private float getSuggestionWeight(final int index) {
- if (index == mCenterSuggestionIndex) {
- return mCenterSuggestionWeight;
- } else {
- // TODO: Revisit this for cases of 5 or more suggestions
- return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1);
- }
- }
-
- private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) {
- mTexts.clear();
- final int count = Math.min(suggestedWords.size(), countInStrip);
- for (int pos = 0; pos < count; pos++) {
- final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos);
- mTexts.add(styled);
- }
- for (int pos = count; pos < countInStrip; pos++) {
- // Make this inactive for touches in layout().
- mTexts.add(null);
- }
- }
-
- private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
- final ViewGroup stripView) {
- final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
- for (int index = 0; index < countInStrip; index++) {
- if (index != 0) {
- // Add divider if this isn't the left most suggestion in suggestions strip.
- addDivider(stripView, mDividers.get(index));
- }
-
- final TextView word = mWords.get(index);
- word.setEnabled(true);
- word.setTextColor(mColorAutoCorrect);
- final String text = suggestedWords.getWord(index);
- word.setText(text);
- word.setTextScaleX(1.0f);
- word.setCompoundDrawables(null, null, null, null);
- stripView.addView(word);
- setLayoutWeight(word, 1.0f, mSuggestionsStripHeight);
- }
- mMoreSuggestionsAvailable = false;
- }
-
- public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView,
- final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
- final int width = stripWidth - mDividerWidth - mPadding * 2;
-
- final TextView wordView = mWordToSaveView;
- wordView.setTextColor(mColorTypedWord);
- final int wordWidth = (int)(width * mCenterSuggestionWeight);
- final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint());
- final float wordScaleX = wordView.getTextScaleX();
- wordView.setTag(word);
- wordView.setText(text);
- wordView.setTextScaleX(wordScaleX);
- stripView.addView(wordView);
- setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
-
- stripView.addView(mDividers.get(0));
-
- final TextView leftArrowView = mLeftwardsArrowView;
- leftArrowView.setTextColor(mColorAutoCorrect);
- leftArrowView.setText(LEFTWARDS_ARROW);
- stripView.addView(leftArrowView);
-
- final TextView hintView = mHintToSaveView;
- hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
- hintView.setTextColor(mColorAutoCorrect);
- final int hintWidth = width - wordWidth - leftArrowView.getWidth();
- final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
- hintView.setText(hintText);
- hintView.setTextScaleX(hintScaleX);
- stripView.addView(hintView);
- setLayoutWeight(
- hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
-
- wordView.setOnClickListener(listener);
- leftArrowView.setOnClickListener(listener);
- hintView.setOnClickListener(listener);
- }
-
- public CharSequence getAddToDictionaryWord() {
- return (CharSequence)mWordToSaveView.getTag();
- }
-
- public boolean isAddToDictionaryShowing(final View v) {
- return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
- }
-
- private static void setLayoutWeight(final View v, final float weight, final int height) {
- final ViewGroup.LayoutParams lp = v.getLayoutParams();
- if (lp instanceof LinearLayout.LayoutParams) {
- final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
- llp.weight = weight;
- llp.width = 0;
- llp.height = height;
- }
- }
-
- private static float getTextScaleX(final CharSequence text, final int maxWidth,
- final TextPaint paint) {
- paint.setTextScaleX(1.0f);
- final int width = getTextWidth(text, paint);
- if (width <= maxWidth) {
- return 1.0f;
- }
- return maxWidth / (float)width;
- }
-
- private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
- final TextPaint paint) {
- if (text == null) return null;
- paint.setTextScaleX(1.0f);
- final int width = getTextWidth(text, paint);
- if (width <= maxWidth) {
- return text;
- }
- final float scaleX = maxWidth / (float)width;
- if (scaleX >= MIN_TEXT_XSCALE) {
- paint.setTextScaleX(scaleX);
- return text;
- }
-
- // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
- // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
- final CharSequence ellipsized = TextUtils.ellipsize(
- text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
- paint.setTextScaleX(MIN_TEXT_XSCALE);
- return ellipsized;
- }
-
- private static int getTextWidth(final CharSequence text, final TextPaint paint) {
- if (TextUtils.isEmpty(text)) return 0;
- final Typeface savedTypeface = paint.getTypeface();
- paint.setTypeface(getTextTypeface(text));
- final int len = text.length();
- final float[] widths = new float[len];
- final int count = paint.getTextWidths(text, 0, len, widths);
- int width = 0;
- for (int i = 0; i < count; i++) {
- width += Math.round(widths[i] + 0.5f);
- }
- paint.setTypeface(savedTypeface);
- return width;
- }
-
- private static Typeface getTextTypeface(final CharSequence text) {
- if (!(text instanceof SpannableString))
- return Typeface.DEFAULT;
-
- final SpannableString ss = (SpannableString)text;
- final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
- if (styles.length == 0)
- return Typeface.DEFAULT;
-
- switch (styles[0].getStyle()) {
- case Typeface.BOLD: return Typeface.DEFAULT_BOLD;
- // TODO: BOLD_ITALIC, ITALIC case?
- default: return Typeface.DEFAULT;
- }
- }
- }
+ private final SuggestionStripLayoutHelper mLayoutHelper;
/**
* Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
@@ -582,16 +96,16 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
word.setTag(pos);
word.setOnClickListener(this);
word.setOnLongClickListener(this);
- mWords.add(word);
+ mWordViews.add(word);
final View divider = inflater.inflate(R.layout.suggestion_divider, null);
divider.setTag(pos);
divider.setOnClickListener(this);
- mDividers.add(divider);
- mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
+ mDividerViews.add(divider);
+ mDebugInfoViews.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
}
- mParams = new SuggestionStripViewParams(
- context, attrs, defStyle, mWords, mDividers, mInfos);
+ mLayoutHelper = new SuggestionStripLayoutHelper(
+ context, attrs, defStyle, mWordViews, mDividerViews, mDebugInfoViews);
mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
@@ -617,24 +131,25 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
public void setSuggestions(final SuggestedWords suggestedWords) {
clear();
mSuggestedWords = suggestedWords;
- mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
+ mLayoutHelper.layout(mSuggestedWords, mSuggestionsStrip, this);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
}
}
public int setMoreSuggestionsHeight(final int remainingHeight) {
- return mParams.setMoreSuggestionsHeight(remainingHeight);
+ return mLayoutHelper.setMoreSuggestionsHeight(remainingHeight);
}
public boolean isShowingAddToDictionaryHint() {
return mSuggestionsStrip.getChildCount() > 0
- && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
+ && mLayoutHelper.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
}
public void showAddToDictionaryHint(final String word, final CharSequence hintText) {
clear();
- mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this);
+ mLayoutHelper.layoutAddToDictionaryHint(
+ word, mSuggestionsStrip, getWidth(), hintText, this);
}
public boolean dismissAddToDictionaryHint() {
@@ -689,7 +204,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public boolean onLongClick(final View view) {
- KeyboardSwitcher.getInstance().hapticAndAudioFeedback(Constants.NOT_A_CODE);
+ AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(
+ Constants.NOT_A_CODE, this);
return showMoreSuggestions();
}
@@ -698,30 +214,30 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (parentKeyboard == null) {
return false;
}
- final SuggestionStripViewParams params = mParams;
- if (!params.mMoreSuggestionsAvailable) {
+ final SuggestionStripLayoutHelper layoutHelper = mLayoutHelper;
+ if (!layoutHelper.mMoreSuggestionsAvailable) {
return false;
}
final int stripWidth = getWidth();
final View container = mMoreSuggestionsContainer;
final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
- builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
- (int)(maxWidth * params.mMinMoreSuggestionsWidth),
- params.getMaxMoreSuggestionsRow(), parentKeyboard);
+ builder.layout(mSuggestedWords, layoutHelper.mSuggestionsCountInStrip, maxWidth,
+ (int)(maxWidth * layoutHelper.mMinMoreSuggestionsWidth),
+ layoutHelper.getMaxMoreSuggestionsRow(), parentKeyboard);
mMoreSuggestionsView.setKeyboard(builder.build());
container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
final int pointX = stripWidth / 2;
- final int pointY = -params.mMoreSuggestionsBottomGap;
+ final int pointY = -layoutHelper.mMoreSuggestionsBottomGap;
moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
mMoreSuggestionsListener);
mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
mOriginX = mLastX;
mOriginY = mLastY;
- for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
- mWords.get(i).setPressed(false);
+ for (int i = 0; i < layoutHelper.mSuggestionsCountInStrip; i++) {
+ mWordViews.get(i).setPressed(false);
}
return true;
}
@@ -791,18 +307,20 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public void onClick(final View view) {
- if (mParams.isAddToDictionaryShowing(view)) {
- mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
+ if (mLayoutHelper.isAddToDictionaryShowing(view)) {
+ mListener.addWordToUserDictionary(mLayoutHelper.getAddToDictionaryWord().toString());
clear();
return;
}
final Object tag = view.getTag();
- if (!(tag instanceof Integer))
+ if (!(tag instanceof Integer)) {
return;
+ }
final int index = (Integer) tag;
- if (index >= mSuggestedWords.size())
+ if (index >= mSuggestedWords.size()) {
return;
+ }
final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(index);
mListener.pickSuggestionManually(index, wordInfo);