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/LatinIME.java29
-rw-r--r--java/src/com/android/inputmethod/latin/ResourceUtils.java135
-rw-r--r--java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java2
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java17
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java83
5 files changed, 202 insertions, 64 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index fdd470cf1..347a4c63a 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -90,7 +90,7 @@ import java.util.TreeSet;
/**
* Input method implementation for Qwerty'ish keyboard.
*/
-public final class LatinIME extends InputMethodService implements KeyboardActionListener,
+public class LatinIME extends InputMethodService implements KeyboardActionListener,
SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener,
Suggest.SuggestInitializationListener {
private static final String TAG = LatinIME.class.getSimpleName();
@@ -188,6 +188,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Keeps track of most recently inserted text (multi-character key) for reverting
private String mEnteredText;
+ // TODO: This boolean is persistent state and causes large side effects at unexpected times.
+ // Find a way to remove it for readability.
private boolean mIsAutoCorrectionIndicatorOn;
private AlertDialog mOptionsDialog;
@@ -902,7 +904,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// we know for sure the cursor moved while we were composing and we should reset
// the state.
final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
- if (!mExpectingUpdateSelection
+ // If the keyboard is not visible, we don't need to do all the housekeeping work, as it
+ // will be reset when the keyboard shows up anyway.
+ // TODO: revisit this when LatinIME supports hardware keyboards.
+ // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown().
+ // TODO: find a better way to simulate actual execution.
+ if (isInputViewShown() && !mExpectingUpdateSelection
&& !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
// TAKE CARE: there is a race condition when we enter this test even when the user
// did not explicitly move the cursor. This happens when typing fast, where two keys
@@ -1752,9 +1759,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Called from PointerTracker through the KeyboardActionListener interface
@Override
+ public void onFinishSlidingInput() {
+ // User finished sliding input.
+ mKeyboardSwitcher.onFinishSlidingInput();
+ }
+
+ // Called from PointerTracker through the KeyboardActionListener interface
+ @Override
public void onCancelInput() {
// User released a finger outside any key
- mKeyboardSwitcher.onCancelInput();
+ // Nothing to do so far.
}
@Override
@@ -2500,7 +2514,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
// We never want to auto-correct on a resumed suggestion. Please refer to the three
- // places above where suggestedWords is affected.
+ // places above where suggestedWords is affected. We also need to reset
+ // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it.
+ // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition)
+ mIsAutoCorrectionIndicatorOn = false;
showSuggestionStrip(suggestedWords, typedWord);
}
@@ -2612,8 +2629,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Callback called by PointerTracker through the KeyboardActionListener. This is called when a
// key is depressed; release matching call is onReleaseKey below.
@Override
- public void onPressKey(final int primaryCode) {
- mKeyboardSwitcher.onPressKey(primaryCode);
+ public void onPressKey(final int primaryCode, final boolean isSinglePointer) {
+ mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
}
// Callback by PointerTracker through the KeyboardActionListener. This is called when a key
diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java
index b74b979b5..a9fba5348 100644
--- a/java/src/com/android/inputmethod/latin/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java
@@ -19,14 +19,17 @@ package com.android.inputmethod.latin;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
+import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.util.ArrayList;
import java.util.HashMap;
public final class ResourceUtils {
private static final String TAG = ResourceUtils.class.getSimpleName();
- private static final boolean DEBUG = false;
public static final float UNDEFINED_RATIO = -1.0f;
public static final int UNDEFINED_DIMENSION = -1;
@@ -35,11 +38,32 @@ public final class ResourceUtils {
// This utility class is not publicly instantiable.
}
- private static final String DEFAULT_PREFIX = "DEFAULT,";
- private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
private static final HashMap<String, String> sDeviceOverrideValueMap =
CollectionUtils.newHashMap();
+ private static final String[] BUILD_KEYS_AND_VALUES = {
+ "HARDWARE", Build.HARDWARE,
+ "MODEL", Build.MODEL,
+ "BRAND", Build.BRAND,
+ "MANUFACTURER", Build.MANUFACTURER
+ };
+ private static final HashMap<String, String> sBuildKeyValues;
+ private static final String sBuildKeyValuesDebugString;
+
+ static {
+ sBuildKeyValues = CollectionUtils.newHashMap();
+ final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList();
+ final int keyCount = BUILD_KEYS_AND_VALUES.length / 2;
+ for (int i = 0; i < keyCount; i++) {
+ final int index = i * 2;
+ final String key = BUILD_KEYS_AND_VALUES[index];
+ final String value = BUILD_KEYS_AND_VALUES[index + 1];
+ sBuildKeyValues.put(key, value);
+ keyValuePairs.add(key + '=' + value);
+ }
+ sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]";
+ }
+
public static String getDeviceOverrideValue(final Resources res, final int overrideResId) {
final int orientation = res.getConfiguration().orientation;
final String key = overrideResId + "-" + orientation;
@@ -48,33 +72,114 @@ public final class ResourceUtils {
}
final String[] overrideArray = res.getStringArray(overrideResId);
- final String overrideValue = StringUtils.findPrefixedString(HARDWARE_PREFIX, overrideArray);
+ final String overrideValue = findConstantForKeyValuePairs(sBuildKeyValues, overrideArray);
// The overrideValue might be an empty string.
if (overrideValue != null) {
- if (DEBUG) {
- Log.d(TAG, "Find override value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " Build.HARDWARE=" + Build.HARDWARE + " override=" + overrideValue);
- }
+ Log.i(TAG, "Find override value:"
+ + " resource="+ res.getResourceEntryName(overrideResId)
+ + " build=" + sBuildKeyValuesDebugString
+ + " override=" + overrideValue);
sDeviceOverrideValueMap.put(key, overrideValue);
return overrideValue;
}
- final String defaultValue = StringUtils.findPrefixedString(DEFAULT_PREFIX, overrideArray);
+ 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.HARDWARE=" + Build.HARDWARE);
- } else if (DEBUG) {
- Log.d(TAG, "Found default value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " Build.HARDWARE=" + Build.HARDWARE + " default=" + defaultValue);
+ + " build=" + sBuildKeyValuesDebugString);
+ } else {
+ Log.i(TAG, "Found default value:"
+ + " resource="+ res.getResourceEntryName(overrideResId)
+ + " build=" + sBuildKeyValuesDebugString
+ + " default=" + defaultValue);
}
sDeviceOverrideValueMap.put(key, defaultValue);
return defaultValue;
}
+ /**
+ * Find the condition that fulfills specified key value pairs from an array of
+ * "condition,constant", and return the corresponding string constant. A condition is
+ * "pattern1[:pattern2...] (or an empty string for the default). A pattern is
+ * "key=regexp_value" string. The condition matches only if all patterns of the condition
+ * are true for the specified key value pairs.
+ *
+ * For example, "condition,constant" has the following format.
+ * (See {@link ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp()})
+ * - HARDWARE=mako,constantForNexus4
+ * - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4
+ * - ,defaultConstant
+ *
+ * @param keyValuePairs attributes to be used to look for a matched condition.
+ * @param conditionConstantArray an array of "condition,constant" elements to be searched.
+ * @return the constant part of the matched "condition,constant" element. Returns null if no
+ * condition matches.
+ */
+ @UsedForTesting
+ static String findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs,
+ final String[] conditionConstantArray) {
+ if (conditionConstantArray == null || keyValuePairs == null) {
+ return null;
+ }
+ for (final String conditionConstant : conditionConstantArray) {
+ final int posComma = conditionConstant.indexOf(',');
+ if (posComma < 0) {
+ throw new RuntimeException("Array element has no comma: " + conditionConstant);
+ }
+ final String condition = conditionConstant.substring(0, posComma);
+ if (condition.isEmpty()) {
+ // Default condition. The default condition should be searched by
+ // {@link #findConstantForDefault(String[])}.
+ continue;
+ }
+ if (fulfillsCondition(keyValuePairs, condition)) {
+ return conditionConstant.substring(posComma + 1);
+ }
+ }
+ return null;
+ }
+
+ private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs,
+ final String condition) {
+ final String[] patterns = condition.split(":");
+ // Check all patterns in a condition are true
+ for (final String pattern : patterns) {
+ final int posEqual = pattern.indexOf('=');
+ if (posEqual < 0) {
+ throw new RuntimeException("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);
+ }
+ final String patternRegexpValue = pattern.substring(posEqual + 1);
+ if (!value.matches(patternRegexpValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @UsedForTesting
+ static String findDefaultConstant(final String[] conditionConstantArray) {
+ 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);
+ }
+ if (posComma == 0) { // condition is empty.
+ return condition.substring(posComma + 1);
+ }
+ }
+ return null;
+ }
+
public static boolean isValidFraction(final float fraction) {
return fraction >= 0.0f;
}
diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
index 9819a02ef..7c4156c48 100644
--- a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
+++ b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
@@ -59,7 +59,7 @@ public final class SeekBarDialogPreference extends DialogPreference
public void setInterface(final ValueProxy proxy) {
mValueProxy = proxy;
- setSummary(getValueText(proxy.readValue(getKey())));
+ setSummary(getValueText(clipValue(proxy.readValue(getKey()))));
}
private String getValueText(final int value) {
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index d5ee58a63..ab050d7a3 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -65,23 +65,6 @@ public final class StringUtils {
}
/**
- * Find a string that start with specified prefix from an array.
- *
- * @param prefix a prefix string to find.
- * @param array an string array to be searched.
- * @return the rest part of the string that starts with the prefix.
- * Returns null if it couldn't be found.
- */
- public static String findPrefixedString(final String prefix, final String[] array) {
- for (final String element : array) {
- if (element.startsWith(prefix)) {
- return element.substring(prefix.length());
- }
- }
- return null;
- }
-
- /**
* Remove duplicates from an array of strings.
*
* This method will always keep the first occurrence of all strings at their position
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index 8f6a2e566..78a6478c6 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -46,10 +46,14 @@ import java.util.ArrayList;
public final class SetupWizardActivity extends Activity implements View.OnClickListener {
static final String TAG = SetupWizardActivity.class.getSimpleName();
+ private static final boolean ENABLE_WELCOME_VIDEO = true;
+
+ private View mSetupWizard;
private View mWelcomeScreen;
private View mSetupScreen;
private Uri mWelcomeVideoUri;
private VideoView mWelcomeVideoView;
+ private ImageView mWelcomeImageView;
private View mActionStart;
private View mActionNext;
private TextView mStep1Bullet;
@@ -57,6 +61,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
private SetupStepGroup mSetupStepGroup;
private static final String STATE_STEP = "step";
private int mStepNumber;
+ private boolean mNeedsToAdjustStepNumberToSystemState;
private static final int STEP_WELCOME = 0;
private static final int STEP_1 = 1;
private static final int STEP_2 = 2;
@@ -64,7 +69,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
private static final int STEP_LAUNCHING_IME_SETTINGS = 4;
private static final int STEP_BACK_FROM_IME_SETTINGS = 5;
- private final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this);
+ final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this);
static final class SettingsPoolingHandler
extends StaticInnerHandlerWrapper<SetupWizardActivity> {
@@ -104,10 +109,11 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
@Override
protected void onCreate(final Bundle savedInstanceState) {
- setTheme(android.R.style.Theme_DeviceDefault_Light_NoActionBar);
+ setTheme(android.R.style.Theme_Translucent_NoTitleBar);
super.onCreate(savedInstanceState);
setContentView(R.layout.setup_wizard);
+ mSetupWizard = findViewById(R.id.setup_wizard);
RichInputMethodManager.init(this);
@@ -154,9 +160,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
step2.setAction(new Runnable() {
@Override
public void run() {
- // Invoke input method picker.
- RichInputMethodManager.getInstance().getInputMethodManager()
- .showInputMethodPicker();
+ invokeInputMethodPicker();
}
});
mSetupStepGroup.addStep(step2);
@@ -179,27 +183,26 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
.authority(getPackageName())
.path(Integer.toString(R.raw.setup_welcome_video))
.build();
- mWelcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video);
- mWelcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ final VideoView welcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video);
+ welcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(final MediaPlayer mp) {
// Now VideoView has been laid-out and ready to play, remove background of it to
// reveal the video.
- mWelcomeVideoView.setBackgroundResource(0);
+ welcomeVideoView.setBackgroundResource(0);
mp.setLooping(true);
}
});
- final ImageView welcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image);
- mWelcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ welcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra);
- mWelcomeVideoView.setVisibility(View.GONE);
- welcomeImageView.setImageResource(R.raw.setup_welcome_image);
- welcomeImageView.setVisibility(View.VISIBLE);
+ hideWelcomeVideoAndShowWelcomeImage();
return true;
}
});
+ mWelcomeVideoView = welcomeVideoView;
+ mWelcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image);
mActionStart = findViewById(R.id.setup_start_label);
mActionStart.setOnClickListener(this);
@@ -234,13 +237,14 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
}
}
- private void invokeSetupWizardOfThisIme() {
+ void invokeSetupWizardOfThisIme() {
final Intent intent = new Intent();
intent.setClass(this, SetupWizardActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
+ mNeedsToAdjustStepNumberToSystemState = true;
}
private void invokeSettingsOfThisIme() {
@@ -251,14 +255,22 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
startActivity(intent);
}
- private void invokeLanguageAndInputSettings() {
+ void invokeLanguageAndInputSettings() {
final Intent intent = new Intent();
intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
+ mNeedsToAdjustStepNumberToSystemState = true;
+ }
+
+ void invokeInputMethodPicker() {
+ // Invoke input method picker.
+ RichInputMethodManager.getInstance().getInputMethodManager()
+ .showInputMethodPicker();
+ mNeedsToAdjustStepNumberToSystemState = true;
}
- private void invokeSubtypeEnablerOfThisIme() {
+ void invokeSubtypeEnablerOfThisIme() {
final InputMethodInfo imi =
RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme();
final Intent intent = new Intent();
@@ -309,6 +321,9 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
@Override
protected void onRestart() {
super.onRestart();
+ // Probably the setup wizard has been invoked from "Recent" menu. The setup step number
+ // needs to be adjusted to system state, because the state (IME is enabled and/or current)
+ // may have been changed.
if (isInSetupSteps(mStepNumber)) {
mStepNumber = determineSetupStepNumber();
}
@@ -318,6 +333,8 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
protected void onResume() {
super.onResume();
if (mStepNumber == STEP_LAUNCHING_IME_SETTINGS) {
+ // Prevent white screen flashing while launching settings activity.
+ mSetupWizard.setVisibility(View.INVISIBLE);
invokeSettingsOfThisIme();
mStepNumber = STEP_BACK_FROM_IME_SETTINGS;
return;
@@ -339,37 +356,53 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
super.onBackPressed();
}
- private static void hideAndStopVideo(final VideoView videoView) {
- videoView.stopPlayback();
- videoView.setVisibility(View.INVISIBLE);
+ void hideWelcomeVideoAndShowWelcomeImage() {
+ mWelcomeVideoView.setVisibility(View.GONE);
+ mWelcomeImageView.setImageResource(R.raw.setup_welcome_image);
+ mWelcomeImageView.setVisibility(View.VISIBLE);
+ }
+
+ private void showAndStartWelcomeVideo() {
+ mWelcomeVideoView.setVisibility(View.VISIBLE);
+ mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
+ mWelcomeVideoView.start();
+ }
+
+ private void hideAndStopWelcomeVideo() {
+ mWelcomeVideoView.stopPlayback();
+ mWelcomeVideoView.setVisibility(View.GONE);
}
@Override
protected void onPause() {
- hideAndStopVideo(mWelcomeVideoView);
+ hideAndStopWelcomeVideo();
super.onPause();
}
@Override
public void onWindowFocusChanged(final boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
- if (hasFocus && isInSetupSteps(mStepNumber)) {
+ if (hasFocus && mNeedsToAdjustStepNumberToSystemState) {
+ mNeedsToAdjustStepNumberToSystemState = false;
mStepNumber = determineSetupStepNumber();
updateSetupStepView();
}
}
private void updateSetupStepView() {
+ mSetupWizard.setVisibility(View.VISIBLE);
final boolean welcomeScreen = (mStepNumber == STEP_WELCOME);
mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE);
mSetupScreen.setVisibility(welcomeScreen ? View.GONE : View.VISIBLE);
if (welcomeScreen) {
- mWelcomeVideoView.setVisibility(View.VISIBLE);
- mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
- mWelcomeVideoView.start();
+ if (ENABLE_WELCOME_VIDEO) {
+ showAndStartWelcomeVideo();
+ } else {
+ hideWelcomeVideoAndShowWelcomeImage();
+ }
return;
}
- hideAndStopVideo(mWelcomeVideoView);
+ hideAndStopWelcomeVideo();
final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber();
mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone);
mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE);