diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
18 files changed, 357 insertions, 1576 deletions
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index e79db367c..1242967ad 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -19,6 +19,13 @@ package com.android.inputmethod.latin; import android.view.inputmethod.EditorInfo; public final class Constants { + public static final class Color { + /** + * The alpha value for fully opaque. + */ + public final static int ALPHA_OPAQUE = 255; + } + public static final class ImeOption { /** * The private IME option used to indicate that no microphone should be shown for a given diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java index b882a4860..1461c0240 100644 --- a/java/src/com/android/inputmethod/latin/ImfUtils.java +++ b/java/src/com/android/inputmethod/latin/ImfUtils.java @@ -90,6 +90,13 @@ public class ImfUtils { return false; } + public static InputMethodSubtype getCurrentInputMethodSubtype(Context context, + InputMethodSubtype defaultSubtype) { + final InputMethodManager imm = getInputMethodManager(context); + final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); + return (currentSubtype != null) ? currentSubtype : defaultSubtype; + } + public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context, final boolean shouldIncludeAuxiliarySubtypes) { final InputMethodManager imm = getInputMethodManager(context); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 9c32f947c..e561f5956 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -63,7 +63,7 @@ public class InputAttributes { final boolean flagAutoComplete = 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); - // Make sure that passwords are not displayed in {@link SuggestionsView}. + // Make sure that passwords are not displayed in {@link SuggestionStripView}. if (InputTypeUtils.isPasswordInputType(inputType) || InputTypeUtils.isVisiblePasswordInputType(inputType) || InputTypeUtils.isEmailVariation(variation) diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index 5ad53480f..cbc916a7e 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -16,14 +16,21 @@ package com.android.inputmethod.latin; -import java.util.Arrays; - // TODO: This class is not thread-safe. public class InputPointers { - private final ScalableIntArray mXCoordinates = new ScalableIntArray(); - private final ScalableIntArray mYCoordinates = new ScalableIntArray(); - private final ScalableIntArray mPointerIds = new ScalableIntArray(); - private final ScalableIntArray mTimes = new ScalableIntArray(); + private final int mDefaultCapacity; + private final ResizableIntArray mXCoordinates; + private final ResizableIntArray mYCoordinates; + private final ResizableIntArray mPointerIds; + private final ResizableIntArray mTimes; + + public InputPointers(int defaultCapacity) { + mDefaultCapacity = defaultCapacity; + mXCoordinates = new ResizableIntArray(defaultCapacity); + mYCoordinates = new ResizableIntArray(defaultCapacity); + mPointerIds = new ResizableIntArray(defaultCapacity); + mTimes = new ResizableIntArray(defaultCapacity); + } public void addPointer(int index, int x, int y, int pointerId, int time) { mXCoordinates.add(index, x); @@ -60,17 +67,42 @@ public class InputPointers { * @param length the number of pointers to be appended. */ public void append(InputPointers src, int startPos, int length) { + if (length == 0) { + return; + } mXCoordinates.append(src.mXCoordinates, startPos, length); mYCoordinates.append(src.mYCoordinates, startPos, length); mPointerIds.append(src.mPointerIds, startPos, length); mTimes.append(src.mTimes, startPos, length); } + /** + * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray} + * to the end of this. + * @param pointerId the pointer id of the source. + * @param times the source {@link ResizableIntArray} to read the event times from. + * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from. + * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from. + * @param startPos the starting index of the data in {@code times} and etc. + * @param length the number of data to be appended. + */ + public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates, + ResizableIntArray yCoordinates, int startPos, int length) { + if (length == 0) { + return; + } + mXCoordinates.append(xCoordinates, startPos, length); + mYCoordinates.append(yCoordinates, startPos, length); + mPointerIds.fill(pointerId, startPos, length); + mTimes.append(times, startPos, length); + } + public void reset() { - mXCoordinates.reset(); - mYCoordinates.reset(); - mPointerIds.reset(); - mTimes.reset(); + final int defaultCapacity = mDefaultCapacity; + mXCoordinates.reset(defaultCapacity); + mYCoordinates.reset(defaultCapacity); + mPointerIds.reset(defaultCapacity); + mTimes.reset(defaultCapacity); } public int getPointerSize() { @@ -92,73 +124,4 @@ public class InputPointers { public int[] getTimes() { return mTimes.getPrimitiveArray(); } - - private static class ScalableIntArray { - private static final int DEFAULT_SIZE = BinaryDictionary.MAX_WORD_LENGTH; - private int[] mArray; - private int mLength; - - public ScalableIntArray() { - reset(); - } - - public void add(int index, int val) { - if (mLength < index + 1) { - mLength = index; - add(val); - } else { - mArray[index] = val; - } - } - - public void add(int val) { - final int nextLength = mLength + 1; - ensureCapacity(nextLength); - mArray[mLength] = val; - mLength = nextLength; - } - - private void ensureCapacity(int minimumCapacity) { - if (mArray.length < minimumCapacity) { - final int nextCapacity = mArray.length * 2; - // The following is the same as newLength = Math.max(minimumCapacity, nextCapacity); - final int newLength = minimumCapacity > nextCapacity - ? minimumCapacity - : nextCapacity; - mArray = Arrays.copyOf(mArray, newLength); - } - } - - public int getLength() { - return mLength; - } - - public void reset() { - mArray = new int[DEFAULT_SIZE]; - mLength = 0; - } - - public int[] getPrimitiveArray() { - return mArray; - } - - public void set(ScalableIntArray ip) { - mArray = ip.mArray; - mLength = ip.mLength; - } - - public void copy(ScalableIntArray ip) { - ensureCapacity(ip.mLength); - System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); - mLength = ip.mLength; - } - - public void append(ScalableIntArray src, int startPos, int length) { - final int currentLength = mLength; - final int newLength = currentLength + length; - ensureCapacity(newLength); - System.arraycopy(src.mArray, startPos, mArray, currentLength, length); - mLength = newLength; - } - } } diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java index 0dcb811b5..c15f45345 100644 --- a/java/src/com/android/inputmethod/latin/InputView.java +++ b/java/src/com/android/inputmethod/latin/InputView.java @@ -24,7 +24,7 @@ import android.view.View; import android.widget.LinearLayout; public class InputView extends LinearLayout { - private View mSuggestionsContainer; + private View mSuggestionStripContainer; private View mKeyboardView; private int mKeyboardTopPadding; @@ -43,13 +43,13 @@ public class InputView extends LinearLayout { @Override protected void onFinishInflate() { - mSuggestionsContainer = findViewById(R.id.suggestions_container); + mSuggestionStripContainer = findViewById(R.id.suggestions_container); mKeyboardView = findViewById(R.id.keyboard_view); } @Override public boolean dispatchTouchEvent(MotionEvent me) { - if (mSuggestionsContainer.getVisibility() == VISIBLE + if (mSuggestionStripContainer.getVisibility() == VISIBLE && mKeyboardView.getVisibility() == VISIBLE && forwardTouchEvent(me)) { return true; @@ -57,7 +57,8 @@ public class InputView extends LinearLayout { return super.dispatchTouchEvent(me); } - // The touch events that hit the top padding of keyboard should be forwarded to SuggestionsView. + // The touch events that hit the top padding of keyboard should be forwarded to + // {@link SuggestionStripView}. private boolean forwardTouchEvent(MotionEvent me) { final Rect rect = mInputViewRect; this.getGlobalVisibleRect(rect); @@ -96,7 +97,7 @@ public class InputView extends LinearLayout { } final Rect receivingRect = mEventReceivingRect; - mSuggestionsContainer.getGlobalVisibleRect(receivingRect); + mSuggestionStripContainer.getGlobalVisibleRect(receivingRect); final int translatedX = x - receivingRect.left; final int translatedY; if (y < forwardingLimitY) { @@ -106,7 +107,7 @@ public class InputView extends LinearLayout { translatedY = y - receivingRect.top; } me.setLocation(translatedX, translatedY); - mSuggestionsContainer.dispatchTouchEvent(me); + mSuggestionStripContainer.dispatchTouchEvent(me); return true; } } diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 974af2584..bb39ce4f7 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -45,7 +45,7 @@ public class LastComposedWord { public final String mCommittedWord; public final int mSeparatorCode; public final CharSequence mPrevWord; - public final InputPointers mInputPointers = new InputPointers(); + public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH); private boolean mActive; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 518bcd5ce..d4b59c4cd 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; +import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -38,7 +39,6 @@ import android.os.Debug; import android.os.IBinder; import android.os.Message; import android.os.SystemClock; -import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import android.text.InputType; import android.text.TextUtils; @@ -67,10 +67,11 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.keyboard.LatinKeyboardView; +import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.latin.suggestions.SuggestionsView; +import com.android.inputmethod.latin.suggestions.SuggestionStripView; +import com.android.inputmethod.research.ResearchLogger; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -81,7 +82,7 @@ import java.util.Locale; * Input method implementation for Qwerty'ish keyboard. */ public class LatinIME extends InputMethodService implements KeyboardActionListener, - SuggestionsView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener { + SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener { private static final String TAG = LatinIME.class.getSimpleName(); private static final boolean TRACE = false; private static boolean DEBUG; @@ -125,7 +126,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private View mExtractArea; private View mKeyPreviewBackingView; private View mSuggestionsContainer; - private SuggestionsView mSuggestionsView; + private SuggestionStripView mSuggestionStripView; /* package for tests */ Suggest mSuggest; private CompletionInfo[] mApplicationSpecifiedCompletions; private ApplicationInfo mTargetApplicationInfo; @@ -205,7 +206,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; switch (msg.what) { case MSG_UPDATE_SUGGESTION_STRIP: - latinIme.updateSuggestionsOrPredictions(); + latinIme.updateSuggestionStrip(); break; case MSG_UPDATE_SHIFT_STATE: switcher.updateShiftState(); @@ -548,9 +549,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen .findViewById(android.R.id.extractArea); mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); mSuggestionsContainer = view.findViewById(R.id.suggestions_container); - mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view); - if (mSuggestionsView != null) - mSuggestionsView.setListener(this, view); + mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); + if (mSuggestionStripView != null) + mSuggestionStripView.setListener(this, view); if (LatinImeLogger.sVISUALDEBUG) { mKeyPreviewBackingView.setBackgroundColor(0x10FF0000); } @@ -597,7 +598,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) { super.onStartInputView(editorInfo, restarting); final KeyboardSwitcher switcher = mKeyboardSwitcher; - LatinKeyboardView inputView = switcher.getKeyboardView(); + MainKeyboardView inputView = switcher.getKeyboardView(); if (editorInfo == null) { Log.e(TAG, "Null EditorInfo in onStartInputView()"); @@ -618,7 +619,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0)); } if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().start(); ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs); } if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { @@ -674,8 +674,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen switcher.loadKeyboard(editorInfo, mCurrentSettings); - if (mSuggestionsView != null) - mSuggestionsView.clear(); + if (mSuggestionStripView != null) + mSuggestionStripView.clear(); setSuggestionStripShownInternal( isSuggestionsStripVisible(), /* needsInputViewShown */ false); @@ -711,7 +711,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.commit(); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().stop(); + ResearchLogger.getInstance().latinIME_onFinishInputInternal(); } KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); @@ -862,7 +862,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { - clearSuggestions(); + clearSuggestionStrip(); return; } @@ -878,7 +878,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen false /* isPrediction */); // When in fullscreen mode, show completions generated by the application final boolean isAutoCorrection = false; - setSuggestions(suggestedWords, isAutoCorrection); + setSuggestionStrip(suggestedWords, isAutoCorrection); setAutoCorrectionIndicator(isAutoCorrection); // TODO: is this the right thing to do? What should we auto-correct to in // this case? This says to keep whatever the user typed. @@ -889,7 +889,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { // TODO: Modify this if we support suggestions with hard keyboard if (onEvaluateInputViewShown() && mSuggestionsContainer != null) { - final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView(); + final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView(); final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false; final boolean shouldShowSuggestions = shown && (needsInputViewShown ? inputViewShown : true); @@ -927,7 +927,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen - keyboardHeight; final LayoutParams params = mKeyPreviewBackingView.getLayoutParams(); - params.height = mSuggestionsView.setMoreSuggestionsHeight(remainingHeight); + params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight); mKeyPreviewBackingView.setLayoutParams(params); return params.height; } @@ -950,7 +950,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int extraHeight = extractHeight + backingHeight + suggestionsHeight; int touchY = extraHeight; // Need to set touchable region only if input view is being shown - final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView(); + final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView(); if (keyboardView != null && keyboardView.isShown()) { if (mSuggestionsContainer.getVisibility() == View.VISIBLE) { touchY -= suggestionsHeight; @@ -988,7 +988,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // the composing word, reset the last composed word, tell the inputconnection about it. private void resetEntireInputState() { resetComposingState(true /* alsoResetLastComposedWord */); - clearSuggestions(); + clearSuggestionStrip(); mConnection.finishComposingText(); } @@ -1011,7 +1011,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), separatorCode, prevWord); } - updateSuggestionsOrPredictions(); + updateSuggestionStrip(); } // Called from the KeyboardSwitcher which needs to know auto caps state to display @@ -1091,7 +1091,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET; } - // Callback for the SuggestionsView, to call when the "add to dictionary" hint is pressed. + // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is + // pressed. @Override public boolean addWordToUserDictionary(String word) { mUserDictionary.addWordToUserDictionary(word, 128); @@ -1329,13 +1330,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) { + public void onUpdateBatchInput(InputPointers batchPointers) { mWordComposer.setBatchInputPointers(batchPointers); - return updateSuggestionsOrPredictions(); + final SuggestedWords suggestedWords = getSuggestedWords(); + showSuggestionStrip(suggestedWords, null); } @Override - public void onEndBatchInput(CharSequence text) { + public void onEndBatchInput(InputPointers batchPointers) { + mWordComposer.setBatchInputPointers(batchPointers); + final SuggestedWords suggestedWords = getSuggestedWords(); + showSuggestionStrip(suggestedWords, null); + if (suggestedWords == null || suggestedWords.size() == 0) { + return; + } + final CharSequence text = suggestedWords.getWord(0); + if (TextUtils.isEmpty(text)) { + return; + } mWordComposer.setBatchInputWord(text); mConnection.beginBatchEdit(); if (SPACE_STATE_PHANTOM == mSpaceState) { @@ -1555,7 +1567,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSpaceState = SPACE_STATE_WEAK; } // In case the "add to dictionary" hint was still displayed. - if (null != mSuggestionsView) mSuggestionsView.dismissAddToDictionaryHint(); + if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint(); } mHandler.postUpdateSuggestionStrip(); Utils.Stats.onNonSeparator((char)primaryCode, x, y); @@ -1634,7 +1646,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void handleClose() { commitTyped(LastComposedWord.NOT_A_SEPARATOR); requestHideSelf(0); - LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); + MainKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); } @@ -1642,14 +1654,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: make this private // Outside LatinIME, only used by the test suite. /* package for tests */ boolean isShowingPunctuationList() { - if (mSuggestionsView == null) return false; - return mCurrentSettings.mSuggestPuncList == mSuggestionsView.getSuggestions(); + if (mSuggestionStripView == null) return false; + return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions(); } private boolean isSuggestionsStripVisible() { - if (mSuggestionsView == null) + if (mSuggestionStripView == null) return false; - if (mSuggestionsView.isShowingAddToDictionaryHint()) + if (mSuggestionStripView.isShowingAddToDictionaryHint()) return true; if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation)) return false; @@ -1658,14 +1670,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation); } - private void clearSuggestions() { - setSuggestions(SuggestedWords.EMPTY, false); + private void clearSuggestionStrip() { + setSuggestionStrip(SuggestedWords.EMPTY, false); setAutoCorrectionIndicator(false); } - private void setSuggestions(final SuggestedWords words, final boolean isAutoCorrection) { - if (mSuggestionsView != null) { - mSuggestionsView.setSuggestions(words); + private void setSuggestionStrip(final SuggestedWords words, final boolean isAutoCorrection) { + if (mSuggestionStripView != null) { + mSuggestionStripView.setSuggestions(words); mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection); } } @@ -1681,8 +1693,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - // TODO: rename this method to updateSuggestionStrip or simply updateSuggestions - private SuggestedWords updateSuggestionsOrPredictions() { + private void updateSuggestionStrip() { mHandler.cancelUpdateSuggestionStrip(); // Check if we have a suggestion engine attached. @@ -1692,15 +1703,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "requested!"); mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); } - return null; + return; } - final String typedWord = mWordComposer.getTypedWord(); if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) { setPunctuationSuggestions(); - return null; + return; } + final SuggestedWords suggestedWords = getSuggestedWords(); + final String typedWord = mWordComposer.getTypedWord(); + showSuggestionStrip(suggestedWords, typedWord); + } + + private SuggestedWords getSuggestedWords() { + final String typedWord = mWordComposer.getTypedWord(); // Get the word on which we should search the bigrams. If we are composing a word, it's // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we // should just skip whitespace if any, so 1. @@ -1708,13 +1725,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence prevWord = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, mWordComposer.isComposingWord() ? 2 : 1); - SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, + final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCurrentSettings.mCorrectionEnabled); - suggestedWords = maybeRetrieveOlderSuggestions(typedWord, suggestedWords); - - showSuggestions(suggestedWords, typedWord); - return suggestedWords; + return maybeRetrieveOlderSuggestions(typedWord, suggestedWords); } private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord, @@ -1728,10 +1742,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // revert to suggestions - although it is unclear how we can come here if it's displayed. if (suggestedWords.size() > 1 || typedWord.length() <= 1 || !suggestedWords.mTypedWordValid - || mSuggestionsView.isShowingAddToDictionaryHint()) { + || mSuggestionStripView.isShowingAddToDictionaryHint()) { return suggestedWords; } else { - SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions(); + SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions(); if (previousSuggestions == mCurrentSettings.mSuggestPuncList) { previousSuggestions = SuggestedWords.EMPTY; } @@ -1747,10 +1761,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void showSuggestions(final SuggestedWords suggestedWords, + private void showSuggestionStrip(final SuggestedWords suggestedWords, final CharSequence typedWord) { if (null == suggestedWords || suggestedWords.size() <= 0) { - clearSuggestions(); + clearSuggestionStrip(); return; } final CharSequence autoCorrection; @@ -1765,7 +1779,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mWordComposer.setAutoCorrection(autoCorrection); final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); - setSuggestions(suggestedWords, isAutoCorrection); + setSuggestionStrip(suggestedWords, isAutoCorrection); setAutoCorrectionIndicator(isAutoCorrection); setSuggestionStripShown(isSuggestionsStripVisible()); } @@ -1773,7 +1787,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void commitCurrentAutoCorrection(final int separatorCodePoint) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { - updateSuggestionsOrPredictions(); + updateSuggestionStrip(); } final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); final String typedWord = mWordComposer.getTypedWord(); @@ -1802,11 +1816,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - // Called from SuggestionsView through the SuggestionsView.Listener interface + // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} + // interface @Override public void pickSuggestionManually(final int index, final CharSequence suggestion, final int x, final int y) { - final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); + final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput if (suggestion.length() == 1 && isShowingPunctuationList()) { // Word separators are suggested before the user inputs something. @@ -1838,8 +1853,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mCurrentSettings.isApplicationSpecifiedCompletionsOn() && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { - if (mSuggestionsView != null) { - mSuggestionsView.clear(); + if (mSuggestionStripView != null) { + mSuggestionStripView.clear(); } mKeyboardSwitcher.updateShiftState(); resetComposingState(true /* alsoResetLastComposedWord */); @@ -1872,23 +1887,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mKeyboardSwitcher.updateShiftState(); // We should show the "Touch again to save" hint if the user pressed the first entry - // AND either: - // - There is no dictionary (we know that because we tried to load it => null != mSuggest - // AND mSuggest.hasMainDictionary() is false) - // - There is a dictionary and the word is not in it + // AND it's in none of our current dictionaries (main, user or otherwise). // 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 = index == 0 && mSuggest != null - // If there is no dictionary the hint should be shown. - && (!mSuggest.hasMainDictionary() - // If "suggestion" is not in the dictionary, the hint should be shown. - || !AutoCorrection.isValidWord( - mSuggest.getUnigramDictionaries(), suggestion, true)); + // If the suggestion is not in the dictionary, the hint should be shown. + && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true); Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { - mSuggestionsView.showAddToDictionaryHint(suggestion, mCurrentSettings.mHintToSaveText); + mSuggestionStripView.showAddToDictionaryHint( + suggestion, mCurrentSettings.mHintToSaveText); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. mHandler.postUpdateSuggestionStrip(); @@ -1900,7 +1910,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ private void commitChosenWord(final CharSequence chosenWord, final int commitType, final int separatorCode) { - final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); + final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); if (ProductionFlag.IS_EXPERIMENTAL) { @@ -1918,9 +1928,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void setPunctuationSuggestions() { if (mCurrentSettings.mBigramPredictionEnabled) { - clearSuggestions(); + clearSuggestionStrip(); } else { - setSuggestions(mCurrentSettings.mSuggestPuncList, false); + setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false); } setAutoCorrectionIndicator(false); setSuggestionStripShown(isSuggestionsStripVisible()); @@ -2102,18 +2112,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen }; private void launchSettings() { - launchSettingsClass(SettingsActivity.class); + handleClose(); + launchSubActivity(SettingsActivity.class); } // Called from debug code only public void launchDebugSettings() { - launchSettingsClass(DebugSettingsActivity.class); + handleClose(); + launchSubActivity(DebugSettingsActivity.class); } - private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) { - handleClose(); + public void launchKeyboardedDialogActivity(Class<? extends Activity> activityClass) { + // Put the text in the attached EditText into a safe, saved state before switching to a + // new activity that will also use the soft keyboard. + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + launchSubActivity(activityClass); + } + + private void launchSubActivity(Class<? extends Activity> activityClass) { Intent intent = new Intent(); - intent.setClass(LatinIME.this, settingsClass); + intent.setClass(LatinIME.this, activityClass); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } @@ -2151,7 +2169,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen showOptionDialog(builder.create()); } - /* package */ void showOptionDialog(AlertDialog dialog) { + public void showOptionDialog(AlertDialog dialog) { final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken(); if (windowToken == null) return; diff --git a/java/src/com/android/inputmethod/latin/ResearchLog.java b/java/src/com/android/inputmethod/latin/ResearchLog.java deleted file mode 100644 index 1de5cb36a..000000000 --- a/java/src/com/android/inputmethod/latin/ResearchLog.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.latin; - -import android.content.SharedPreferences; -import android.os.SystemClock; -import android.util.JsonWriter; -import android.util.Log; -import android.view.inputmethod.CompletionInfo; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.ResearchLogger.LogUnit; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * Logs the use of the LatinIME keyboard. - * - * This class logs operations on the IME keyboard, including what the user has typed. - * Data is stored locally in a file in app-specific storage. - * - * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}. - */ -public class ResearchLog { - private static final String TAG = ResearchLog.class.getSimpleName(); - private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( - new OutputStreamWriter(new NullOutputStream())); - - final ScheduledExecutorService mExecutor; - /* package */ final File mFile; - private JsonWriter mJsonWriter = NULL_JSON_WRITER; // should never be null - - private int mLoggingState; - private static final int LOGGING_STATE_UNSTARTED = 0; - private static final int LOGGING_STATE_RUNNING = 1; - private static final int LOGGING_STATE_STOPPING = 2; - private static final int LOGGING_STATE_STOPPED = 3; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; - - private static class NullOutputStream extends OutputStream { - /** {@inheritDoc} */ - @Override - public void write(byte[] buffer, int offset, int count) { - // nop - } - - /** {@inheritDoc} */ - @Override - public void write(byte[] buffer) { - // nop - } - - @Override - public void write(int oneByte) { - } - } - - public ResearchLog(File outputFile) { - mExecutor = Executors.newSingleThreadScheduledExecutor(); - if (outputFile == null) { - throw new IllegalArgumentException(); - } - mFile = outputFile; - mLoggingState = LOGGING_STATE_UNSTARTED; - } - - public synchronized void start() throws IOException { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); - mJsonWriter.setLenient(true); - mJsonWriter.beginArray(); - mLoggingState = LOGGING_STATE_RUNNING; - break; - case LOGGING_STATE_RUNNING: - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - break; - } - } - - public synchronized void stop() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - break; - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.flush(); - mJsonWriter.close(); - } finally { - // the contentprovider only exports data if the writable - // bit is cleared. - boolean success = mFile.setWritable(false, false); - mLoggingState = LOGGING_STATE_STOPPED; - } - return null; - } - }); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public boolean isAlive() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - case LOGGING_STATE_RUNNING: - return true; - } - return false; - } - - public void waitUntilStopped(int timeoutInMs) throws InterruptedException { - mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS); - } - - private boolean isAbortSuccessful; - public boolean isAbortSuccessful() { - return isAbortSuccessful; - } - - public synchronized void abort() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - isAbortSuccessful = true; - break; - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.close(); - } finally { - isAbortSuccessful = mFile.delete(); - } - return null; - } - }); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - /* package */ synchronized void flush() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_RUNNING: - removeAnyScheduledFlush(); - mExecutor.submit(mFlushCallable); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - private Callable<Object> mFlushCallable = new Callable<Object>() { - @Override - public Object call() throws Exception { - mJsonWriter.flush(); - return null; - } - }; - - private ScheduledFuture<Object> mFlushFuture; - - private void removeAnyScheduledFlush() { - if (mFlushFuture != null) { - mFlushFuture.cancel(false); - mFlushFuture = null; - } - } - - private void scheduleFlush() { - removeAnyScheduledFlush(); - mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); - } - - public synchronized void publishPublicEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - logUnit.publishPublicEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public synchronized void publishAllEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - logUnit.publishAllEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; - void outputEvent(final String[] keys, final Object[] values) { - // not thread safe. - try { - mJsonWriter.beginObject(); - mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); - mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis()); - mJsonWriter.name(EVENT_TYPE_KEY).value(keys[0]); - final int length = values.length; - for (int i = 0; i < length; i++) { - mJsonWriter.name(keys[i + 1]); - Object value = values[i]; - if (value instanceof String) { - mJsonWriter.value((String) value); - } else if (value instanceof Number) { - mJsonWriter.value((Number) value); - } else if (value instanceof Boolean) { - mJsonWriter.value((Boolean) value); - } else if (value instanceof CompletionInfo[]) { - CompletionInfo[] ci = (CompletionInfo[]) value; - mJsonWriter.beginArray(); - for (int j = 0; j < ci.length; j++) { - mJsonWriter.value(ci[j].toString()); - } - mJsonWriter.endArray(); - } else if (value instanceof SharedPreferences) { - SharedPreferences prefs = (SharedPreferences) value; - mJsonWriter.beginObject(); - for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) { - mJsonWriter.name(entry.getKey()); - final Object innerValue = entry.getValue(); - if (innerValue == null) { - mJsonWriter.nullValue(); - } else if (innerValue instanceof Boolean) { - mJsonWriter.value((Boolean) innerValue); - } else if (innerValue instanceof Number) { - mJsonWriter.value((Number) innerValue); - } else { - mJsonWriter.value(innerValue.toString()); - } - } - mJsonWriter.endObject(); - } else if (value instanceof Key[]) { - Key[] keyboardKeys = (Key[]) value; - mJsonWriter.beginArray(); - for (Key keyboardKey : keyboardKeys) { - mJsonWriter.beginObject(); - mJsonWriter.name("code").value(keyboardKey.mCode); - mJsonWriter.name("altCode").value(keyboardKey.mAltCode); - mJsonWriter.name("x").value(keyboardKey.mX); - mJsonWriter.name("y").value(keyboardKey.mY); - mJsonWriter.name("w").value(keyboardKey.mWidth); - mJsonWriter.name("h").value(keyboardKey.mHeight); - mJsonWriter.endObject(); - } - mJsonWriter.endArray(); - } else if (value instanceof SuggestedWords) { - SuggestedWords words = (SuggestedWords) value; - mJsonWriter.beginObject(); - mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); - mJsonWriter.name("willAutoCorrect") - .value(words.mWillAutoCorrect); - mJsonWriter.name("isPunctuationSuggestions") - .value(words.mIsPunctuationSuggestions); - mJsonWriter.name("isObsoleteSuggestions") - .value(words.mIsObsoleteSuggestions); - mJsonWriter.name("isPrediction") - .value(words.mIsPrediction); - mJsonWriter.name("words"); - mJsonWriter.beginArray(); - final int size = words.size(); - for (int j = 0; j < size; j++) { - SuggestedWordInfo wordInfo = words.getWordInfo(j); - mJsonWriter.value(wordInfo.toString()); - } - mJsonWriter.endArray(); - mJsonWriter.endObject(); - } else if (value == null) { - mJsonWriter.nullValue(); - } else { - Log.w(TAG, "Unrecognized type to be logged: " + - (value == null ? "<null>" : value.getClass().getName())); - mJsonWriter.nullValue(); - } - } - mJsonWriter.endObject(); - } catch (IOException e) { - e.printStackTrace(); - Log.w(TAG, "Error in JsonWriter; disabling logging"); - try { - mJsonWriter.close(); - } catch (IllegalStateException e1) { - // assume that this is just the json not being terminated properly. - // ignore - } catch (IOException e1) { - e1.printStackTrace(); - } finally { - mJsonWriter = NULL_JSON_WRITER; - } - } - } -} diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java deleted file mode 100644 index 1abfbad13..000000000 --- a/java/src/com/android/inputmethod/latin/ResearchLogger.java +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * Copyright (C) 2012 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 static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.inputmethodservice.InputMethodService; -import android.os.Build; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.util.Log; -import android.view.MotionEvent; -import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.widget.Toast; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.latin.RichInputConnection.Range; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.UUID; - -/** - * Logs the use of the LatinIME keyboard. - * - * This class logs operations on the IME keyboard, including what the user has typed. - * Data is stored locally in a file in app-specific storage. - * - * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}. - */ -public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = ResearchLogger.class.getSimpleName(); - private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info - /* package */ static boolean sIsLogging = false; - private static final int OUTPUT_FORMAT_VERSION = 1; - private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; - private static final String FILENAME_PREFIX = "researchLog"; - private static final String FILENAME_SUFFIX = ".txt"; - private static final SimpleDateFormat TIMESTAMP_DATEFORMAT = - new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); - - // constants related to specific log points - private static final String WHITESPACE_SEPARATORS = " \t\n\r"; - private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 - private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; - private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; - - private static final ResearchLogger sInstance = new ResearchLogger(); - // to write to a different filename, e.g., for testing, set mFile before calling start() - /* package */ File mFilesDir; - /* package */ String mUUIDString; - /* package */ ResearchLog mMainResearchLog; - // The mIntentionalResearchLog records all events for the session, private or not (excepting - // passwords). It is written to permanent storage only if the user explicitly commands - // the system to do so. - /* package */ ResearchLog mIntentionalResearchLog; - // LogUnits are queued here and released only when the user requests the intentional log. - private final List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>(); - - private boolean mIsPasswordView = false; - private boolean mIsLoggingSuspended = false; - - // digits entered by the user are replaced with this codepoint. - /* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT = - Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area" - // U+E001 is in the "private-use area" - /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001"; - private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time"; - private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS; - private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS; - // set when LatinIME should ignore an onUpdateSelection() callback that - // arises from operations in this class - private static boolean sLatinIMEExpectingUpdateSelection = false; - - // used to check whether words are not unique - private Suggest mSuggest; - private Dictionary mDictionary; - private KeyboardSwitcher mKeyboardSwitcher; - private Context mContext; - - private ResearchLogger() { - } - - public static ResearchLogger getInstance() { - return sInstance; - } - - public void init(final InputMethodService ims, final SharedPreferences prefs, - KeyboardSwitcher keyboardSwitcher) { - assert ims != null; - if (ims == null) { - Log.w(TAG, "IMS is null; logging is off"); - } else { - mContext = ims; - mFilesDir = ims.getFilesDir(); - if (mFilesDir == null || !mFilesDir.exists()) { - Log.w(TAG, "IME storage directory does not exist."); - } - } - if (prefs != null) { - mUUIDString = getUUID(prefs); - sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); - prefs.registerOnSharedPreferenceChangeListener(this); - - final long lastCleanupTime = prefs.getLong(PREF_LAST_CLEANUP_TIME, 0L); - final long now = System.currentTimeMillis(); - if (lastCleanupTime + DURATION_BETWEEN_DIR_CLEANUP_IN_MS < now) { - final long timeHorizon = now - MAX_LOGFILE_AGE_IN_MS; - cleanupLoggingDir(mFilesDir, timeHorizon); - Editor e = prefs.edit(); - e.putLong(PREF_LAST_CLEANUP_TIME, now); - e.apply(); - } - } - mKeyboardSwitcher = keyboardSwitcher; - } - - private void cleanupLoggingDir(final File dir, final long time) { - for (File file : dir.listFiles()) { - if (file.getName().startsWith(ResearchLogger.FILENAME_PREFIX) && - file.lastModified() < time) { - file.delete(); - } - } - } - - private File createLogFile(File filesDir) { - final StringBuilder sb = new StringBuilder(); - sb.append(FILENAME_PREFIX).append('-'); - sb.append(mUUIDString).append('-'); - sb.append(TIMESTAMP_DATEFORMAT.format(new Date())); - sb.append(FILENAME_SUFFIX); - return new File(filesDir, sb.toString()); - } - - public void start() { - if (!sIsLogging) { - // Log.w(TAG, "not in usability mode; not logging"); - return; - } - if (mFilesDir == null || !mFilesDir.exists()) { - Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); - return; - } - if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - try { - mMainResearchLog.start(); - if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) { - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mIntentionalResearchLog.start(); - } catch (IOException e) { - Log.w(TAG, "Could not start ResearchLogger."); - } - } - - public void stop() { - if (mMainResearchLog != null) { - mMainResearchLog.stop(); - } - } - - public boolean abort() { - mIsLoggingSuspended = true; - requestIndicatorRedraw(); - boolean didAbortMainLog = false; - if (mMainResearchLog != null) { - mMainResearchLog.abort(); - try { - mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); - } catch (InterruptedException e) { - // interrupted early. carry on. - } - if (mMainResearchLog.isAbortSuccessful()) { - didAbortMainLog = true; - } - } - boolean didAbortIntentionalLog = false; - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.abort(); - try { - mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); - } catch (InterruptedException e) { - // interrupted early. carry on. - } - if (mIntentionalResearchLog.isAbortSuccessful()) { - didAbortIntentionalLog = true; - } - } - return didAbortMainLog && didAbortIntentionalLog; - } - - /* package */ void flush() { - if (mMainResearchLog != null) { - mMainResearchLog.flush(); - } - } - - private void logWholeSessionHistory() throws IOException { - try { - LogUnit headerLogUnit = new LogUnit(); - headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false); - mIntentionalResearchLog.publishAllEvents(headerLogUnit); - for (LogUnit logUnit : mIntentionalResearchLogQueue) { - mIntentionalResearchLog.publishAllEvents(logUnit); - } - mIntentionalResearchLog.stop(); - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - mIntentionalResearchLog.start(); - } finally { - mIntentionalResearchLogQueue.clear(); - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { - if (key == null || prefs == null) { - return; - } - sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); - if (sIsLogging == false) { - abort(); - } - } - - /* package */ void presentResearchDialog(final LatinIME latinIME) { - final CharSequence title = latinIME.getString(R.string.english_ime_research_log); - final CharSequence[] items = new CharSequence[] { - latinIME.getString(R.string.note_timestamp_for_researchlog), - mIsLoggingSuspended ? latinIME.getString(R.string.enable_session_logging) : - latinIME.getString(R.string.do_not_log_this_session), - latinIME.getString(R.string.log_whole_session_history), - }; - final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface di, int position) { - di.dismiss(); - switch (position) { - case 0: - userTimestamp(); - Toast.makeText(latinIME, R.string.notify_recorded_timestamp, - Toast.LENGTH_LONG).show(); - break; - case 1: - if (mIsLoggingSuspended) { - mIsLoggingSuspended = false; - requestIndicatorRedraw(); - Toast toast = Toast.makeText(latinIME, - R.string.notify_session_logging_enabled, Toast.LENGTH_LONG); - } else { - Toast toast = Toast.makeText(latinIME, - R.string.notify_session_log_deleting, Toast.LENGTH_LONG); - toast.show(); - boolean isLogDeleted = abort(); - toast.cancel(); - if (isLogDeleted) { - Toast.makeText(latinIME, R.string.notify_session_log_deleted, - Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(latinIME, - R.string.notify_session_log_not_deleted, Toast.LENGTH_LONG) - .show(); - } - } - break; - case 2: - try { - logWholeSessionHistory(); - Toast.makeText(latinIME, R.string.notify_session_history_logged, - Toast.LENGTH_LONG).show(); - } catch (IOException e) { - Toast.makeText(latinIME, R.string.notify_session_history_not_logged, - Toast.LENGTH_LONG).show(); - e.printStackTrace(); - } - break; - } - } - - }; - final AlertDialog.Builder builder = new AlertDialog.Builder(latinIME) - .setItems(items, listener) - .setTitle(title); - latinIME.showOptionDialog(builder.create()); - } - - public void initSuggest(Suggest suggest) { - mSuggest = suggest; - } - - private void setIsPasswordView(boolean isPasswordView) { - mIsPasswordView = isPasswordView; - } - - private boolean isAllowedToLog() { - return !mIsPasswordView && !mIsLoggingSuspended; - } - - public void requestIndicatorRedraw() { - // invalidate any existing graphics - if (mKeyboardSwitcher != null) { - mKeyboardSwitcher.getKeyboardView().invalidateAllKeys(); - } - } - - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; - private static final Object[] EVENTKEYS_NULLVALUES = {}; - - private LogUnit mCurrentLogUnit = new LogUnit(); - - /** - * Buffer a research log event, flagging it as privacy-sensitive. - * - * This event contains potentially private information. If the word that this event is a part - * of is determined to be privacy-sensitive, then this event should not be included in the - * output log. The system waits to output until the containing word is known. - * - * @param keys an array containing a descriptive name for the event, followed by the keys - * @param values an array of values, either a String or Number. length should be one - * less than the keys array - */ - private synchronized void enqueuePotentiallyPrivateEvent(final String[] keys, - final Object[] values) { - assert values.length + 1 == keys.length; - if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, true); - } - } - - /** - * Buffer a research log event, flaggint it as not privacy-sensitive. - * - * This event contains no potentially private information. Even if the word that this event - * is privacy-sensitive, this event can still safely be sent to the output log. The system - * waits until the containing word is known so that this event can be written in the proper - * temporal order with other events that may be privacy sensitive. - * - * @param keys an array containing a descriptive name for the event, followed by the keys - * @param values an array of values, either a String or Number. length should be one - * less than the keys array - */ - private synchronized void enqueueEvent(final String[] keys, final Object[] values) { - assert values.length + 1 == keys.length; - if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, false); - } - } - - // Used to track how often words are logged. Too-frequent logging can leak - // semantics, disclosing private data. - /* package for test */ static class LoggingFrequencyState { - private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; - private int mWordsRemainingToSkip; - private final int mFrequency; - - /** - * Tracks how often words may be uploaded. - * - * @param frequency 1=Every word, 2=Every other word, etc. - */ - public LoggingFrequencyState(int frequency) { - mFrequency = frequency; - mWordsRemainingToSkip = mFrequency; - } - - public void onWordLogged() { - mWordsRemainingToSkip = mFrequency; - } - - public void onWordNotLogged() { - if (mWordsRemainingToSkip > 1) { - mWordsRemainingToSkip--; - } - } - - public boolean isSafeToLog() { - return mWordsRemainingToSkip <= 1; - } - } - - /* package for test */ LoggingFrequencyState mLoggingFrequencyState = - new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); - - /* package for test */ boolean isPrivacyThreat(String word) { - // Current checks: - // - Word not in dictionary - // - Word contains numbers - // - Privacy-safe word not logged recently - if (TextUtils.isEmpty(word)) { - return false; - } - if (!mLoggingFrequencyState.isSafeToLog()) { - return true; - } - final int length = word.length(); - boolean hasLetter = false; - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = Character.codePointAt(word, i); - if (Character.isDigit(codePoint)) { - return true; - } - if (Character.isLetter(codePoint)) { - hasLetter = true; - break; // Word may contain digits, but will only be allowed if in the dictionary. - } - } - if (hasLetter) { - if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) { - mDictionary = mSuggest.getMainDictionary(); - } - if (mDictionary == null) { - // Can't access dictionary. Assume privacy threat. - return true; - } - return !(mDictionary.isValidWord(word)); - } - // No letters, no numbers. Punctuation, space, or something else. - return false; - } - - private void onWordComplete(String word) { - if (isPrivacyThreat(word)) { - publishLogUnit(mCurrentLogUnit, true); - mLoggingFrequencyState.onWordNotLogged(); - } else { - publishLogUnit(mCurrentLogUnit, false); - mLoggingFrequencyState.onWordLogged(); - } - mCurrentLogUnit = new LogUnit(); - } - - private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { - if (isPrivacySensitive) { - mMainResearchLog.publishPublicEvents(logUnit); - } else { - mMainResearchLog.publishAllEvents(logUnit); - } - mIntentionalResearchLogQueue.add(logUnit); - } - - /* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) { - publishLogUnit(mCurrentLogUnit, isPrivacySensitive); - } - - static class LogUnit { - private final List<String[]> mKeysList = new ArrayList<String[]>(); - private final List<Object[]> mValuesList = new ArrayList<Object[]>(); - private final List<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>(); - - private void addLogAtom(final String[] keys, final Object[] values, - final Boolean isPotentiallyPrivate) { - mKeysList.add(keys); - mValuesList.add(values); - mIsPotentiallyPrivate.add(isPotentiallyPrivate); - } - - public void publishPublicEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - if (!mIsPotentiallyPrivate.get(i)) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); - } - } - } - - public void publishAllEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); - } - } - } - - private static int scrubDigitFromCodePoint(int codePoint) { - return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint; - } - - /* package for test */ static String scrubDigitsFromString(String s) { - StringBuilder sb = null; - final int length = s.length(); - for (int i = 0; i < length; i = s.offsetByCodePoints(i, 1)) { - final int codePoint = Character.codePointAt(s, i); - if (Character.isDigit(codePoint)) { - if (sb == null) { - sb = new StringBuilder(length); - sb.append(s.substring(0, i)); - } - sb.appendCodePoint(DIGIT_REPLACEMENT_CODEPOINT); - } else { - if (sb != null) { - sb.appendCodePoint(codePoint); - } - } - } - if (sb == null) { - return s; - } else { - return sb.toString(); - } - } - - private String scrubWord(String word) { - if (mDictionary == null) { - return WORD_REPLACEMENT_STRING; - } - if (mDictionary.isValidWord(word)) { - return word; - } - return WORD_REPLACEMENT_STRING; - } - - private static final String[] EVENTKEYS_INTENTIONAL_LOG = { - "IntentionalLog" - }; - private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = { - "LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size", - "pressure" - }; - public static void latinKeyboardView_processMotionEvent(final MotionEvent me, final int action, - final long eventTime, final int index, final int id, final int x, final int y) { - if (me != null) { - final String actionString; - switch (action) { - case MotionEvent.ACTION_CANCEL: actionString = "CANCEL"; break; - case MotionEvent.ACTION_UP: actionString = "UP"; break; - case MotionEvent.ACTION_DOWN: actionString = "DOWN"; break; - case MotionEvent.ACTION_POINTER_UP: actionString = "POINTER_UP"; break; - case MotionEvent.ACTION_POINTER_DOWN: actionString = "POINTER_DOWN"; break; - case MotionEvent.ACTION_MOVE: actionString = "MOVE"; break; - case MotionEvent.ACTION_OUTSIDE: actionString = "OUTSIDE"; break; - default: actionString = "ACTION_" + action; break; - } - final float size = me.getSize(index); - final float pressure = me.getPressure(index); - final Object[] values = { - actionString, eventTime, id, x, y, size, pressure - }; - getInstance().enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT, values); - } - } - - private static final String[] EVENTKEYS_LATINIME_ONCODEINPUT = { - "LatinIMEOnCodeInput", "code", "x", "y" - }; - public static void latinIME_onCodeInput(final int code, final int x, final int y) { - final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - } - - private static final String[] EVENTKEYS_CORRECTION = { - "LogCorrection", "subgroup", "before", "after", "position" - }; - public static void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final Object[] values = { - subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { - "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" - }; - public static void latinIME_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection) { - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { - "LatinIMEDeleteSurroundingText", "length" - }; - public static void latinIME_deleteSurroundingText(final int length) { - final Object[] values = { - length - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { - "LatinIMEDoubleSpaceAutoPeriod" - }; - public static void latinIME_doubleSpaceAutoPeriod() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { - "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" - }; - public static void latinIME_onDisplayCompletions( - final CompletionInfo[] applicationSpecifiedCompletions) { - final Object[] values = { - applicationSpecifiedCompletions - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS, - values); - } - - /* package */ static boolean getAndClearLatinIMEExpectingUpdateSelection() { - boolean returnValue = sLatinIMEExpectingUpdateSelection; - sLatinIMEExpectingUpdateSelection = false; - return returnValue; - } - - private static final String[] EVENTKEYS_LATINIME_ONWINDOWHIDDEN = { - "LatinIMEOnWindowHidden", "isTextTruncated", "text" - }; - public static void latinIME_onWindowHidden(final int savedSelectionStart, - final int savedSelectionEnd, final InputConnection ic) { - if (ic != null) { - ic.beginBatchEdit(); - ic.performContextMenuAction(android.R.id.selectAll); - CharSequence charSequence = ic.getSelectedText(0); - ic.setSelection(savedSelectionStart, savedSelectionEnd); - ic.endBatchEdit(); - sLatinIMEExpectingUpdateSelection = true; - final Object[] values = new Object[2]; - if (OUTPUT_ENTIRE_BUFFER) { - if (TextUtils.isEmpty(charSequence)) { - values[0] = false; - values[1] = ""; - } else { - if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) { - int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE; - // do not cut in the middle of a supplementary character - final char c = charSequence.charAt(length - 1); - if (Character.isHighSurrogate(c)) { - length--; - } - final CharSequence truncatedCharSequence = charSequence.subSequence(0, - length); - values[0] = true; - values[1] = truncatedCharSequence.toString(); - } else { - values[0] = false; - values[1] = charSequence.toString(); - } - } - } else { - values[0] = true; - values[1] = ""; - } - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); - // Play it safe. Remove privacy-sensitive events. - researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true); - researchLogger.mCurrentLogUnit = new LogUnit(); - } - } - - private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { - "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", - "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" - }; - public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, - final SharedPreferences prefs) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.start(); - if (editorInfo != null) { - final Context context = researchLogger.mContext; - try { - final PackageInfo packageInfo; - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), - 0); - final Integer versionCode = packageInfo.versionCode; - final String versionName = packageInfo.versionName; - final Object[] values = { - researchLogger.mUUIDString, editorInfo.packageName, - Integer.toHexString(editorInfo.inputType), - Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, - Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName, - OUTPUT_FORMAT_VERSION - }; - researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values); - } catch (NameNotFoundException e) { - e.printStackTrace(); - } - } - } - - private static String getUUID(final SharedPreferences prefs) { - String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null); - if (null == uuidString) { - UUID uuid = UUID.randomUUID(); - uuidString = uuid.toString(); - Editor editor = prefs.edit(); - editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString); - editor.apply(); - } - return uuidString; - } - - private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = { - "LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart", - "oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd", - "expectingUpdateSelection", "expectingUpdateSelectionFromLogger", "context" - }; - public static void latinIME_onUpdateSelection(final int lastSelectionStart, - final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd, final int composingSpanStart, - final int composingSpanEnd, final boolean expectingUpdateSelection, - final boolean expectingUpdateSelectionFromLogger, - final RichInputConnection connection) { - String word = ""; - if (connection != null) { - Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); - if (range != null) { - word = range.mWord; - } - } - final ResearchLogger researchLogger = getInstance(); - final String scrubbedWord = researchLogger.scrubWord(word); - final Object[] values = { - lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, - newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection, - expectingUpdateSelectionFromLogger, scrubbedWord - }; - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { - "LatinIMEPerformEditorAction", "imeActionNext" - }; - public static void latinIME_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { - "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" - }; - public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs, int x, int y) { - final Object[] values = { - index, cs, x, y - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { - "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" - }; - public static void latinIME_pickSuggestionManually(final String replacedWord, - final int index, CharSequence suggestion, int x, int y) { - final Object[] values = { - scrubDigitsFromString(replacedWord), index, suggestion == null ? null : - scrubDigitsFromString(suggestion.toString()), x, y - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, - values); - } - - private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = { - "LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y" - }; - public static void latinIME_punctuationSuggestion(final int index, - final CharSequence suggestion, int x, int y) { - final Object[] values = { - index, suggestion, x, y - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { - "LatinIMERevertDoubleSpaceWhileInBatchEdit" - }; - public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { - "LatinIMERevertSwapPunctuation" - }; - public static void latinIME_revertSwapPunctuation() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { - "LatinIMESendKeyCodePoint", "code" - }; - public static void latinIME_sendKeyCodePoint(final int code) { - final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)) - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); - } - - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { - "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" - }; - public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS = { - "LatinKeyboardViewOnLongPress" - }; - public static void latinKeyboardView_onLongPress() { - getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD = { - "LatinKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width", - "modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey", - "passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled", - "isMultiLine", "tw", "th", "keys" - }; - public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) { - if (keyboard != null) { - final KeyboardId kid = keyboard.mId; - final boolean isPasswordView = kid.passwordInput(); - final Object[] values = { - KeyboardId.elementIdToName(kid.mElementId), - kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), - kid.mOrientation, - kid.mWidth, - KeyboardId.modeName(kid.mMode), - kid.imeAction(), - kid.navigateNext(), - kid.navigatePrevious(), - kid.mClobberSettingsKey, - isPasswordView, - kid.mShortcutKeyEnabled, - kid.mHasShortcutKey, - kid.mLanguageSwitchKeyEnabled, - kid.isMultiLine(), - keyboard.mOccupiedWidth, - keyboard.mOccupiedHeight, - keyboard.mKeys - }; - getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD, values); - getInstance().setIsPasswordView(isPasswordView); - } - } - - private static final String[] EVENTKEYS_LATINIME_REVERTCOMMIT = { - "LatinIMERevertCommit", "originallyTypedWord" - }; - public static void latinIME_revertCommit(final String originallyTypedWord) { - final Object[] values = { - originallyTypedWord - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_REVERTCOMMIT, values); - } - - private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT = { - "PointerTrackerCallListenerOnCancelInput" - }; - public static void pointerTracker_callListenerOnCancelInput() { - getInstance().enqueueEvent(EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT = { - "PointerTrackerCallListenerOnCodeInput", "code", "outputText", "x", "y", - "ignoreModifierKey", "altersCode", "isEnabled" - }; - public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x, - final int y, final boolean ignoreModifierKey, final boolean altersCode, - final int code) { - if (key != null) { - CharSequence outputText = key.mOutputText; - final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null - : scrubDigitsFromString(outputText.toString()), - x, y, ignoreModifierKey, altersCode, key.isEnabled() - }; - getInstance().enqueuePotentiallyPrivateEvent( - EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT, values); - } - } - - private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE = { - "PointerTrackerCallListenerOnRelease", "code", "withSliding", "ignoreModifierKey", - "isEnabled" - }; - public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode, - final boolean withSliding, final boolean ignoreModifierKey) { - if (key != null) { - final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding, - ignoreModifierKey, key.isEnabled() - }; - getInstance().enqueuePotentiallyPrivateEvent( - EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE, values); - } - } - - private static final String[] EVENTKEYS_POINTERTRACKER_ONDOWNEVENT = { - "PointerTrackerOnDownEvent", "deltaT", "distanceSquared" - }; - public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) { - final Object[] values = { - deltaT, distanceSquared - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONDOWNEVENT, values); - } - - private static final String[] EVENTKEYS_POINTERTRACKER_ONMOVEEVENT = { - "PointerTrackerOnMoveEvent", "x", "y", "lastX", "lastY" - }; - public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX, - final int lastY) { - final Object[] values = { - x, y, lastX, lastY - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); - } - - private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { - "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" - }; - public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) { - if (me != null) { - final Object[] values = { - me.toString() - }; - getInstance().enqueuePotentiallyPrivateEvent( - EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, values); - } - } - - private static final String[] EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS = { - "SuggestionsViewSetSuggestions", "suggestedWords" - }; - public static void suggestionsView_setSuggestions(final SuggestedWords suggestedWords) { - if (suggestedWords != null) { - final Object[] values = { - suggestedWords - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS, - values); - } - } - - private static final String[] EVENTKEYS_USER_TIMESTAMP = { - "UserTimestamp" - }; - public void userTimestamp() { - getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); - } -} diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java new file mode 100644 index 000000000..387d45a53 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2012 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 java.util.Arrays; + +// TODO: This class is not thread-safe. +public class ResizableIntArray { + private int[] mArray; + private int mLength; + + public ResizableIntArray(final int capacity) { + reset(capacity); + } + + public int get(final int index) { + if (index < mLength) { + return mArray[index]; + } + throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index); + } + + public void add(final int index, final int val) { + if (index < mLength) { + mArray[index] = val; + } else { + mLength = index; + add(val); + } + } + + public void add(final int val) { + final int currentLength = mLength; + ensureCapacity(currentLength + 1); + mArray[currentLength] = val; + mLength = currentLength + 1; + } + + /** + * Calculate the new capacity of {@code mArray}. + * @param minimumCapacity the minimum capacity that the {@code mArray} should have. + * @return the new capacity that the {@code mArray} should have. Returns zero when there is no + * need to expand {@code mArray}. + */ + private int calculateCapacity(final int minimumCapacity) { + final int currentCapcity = mArray.length; + if (currentCapcity < minimumCapacity) { + final int nextCapacity = currentCapcity * 2; + // The following is the same as return Math.max(minimumCapacity, nextCapacity); + return minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity; + } + return 0; + } + + private void ensureCapacity(final int minimumCapacity) { + final int newCapacity = calculateCapacity(minimumCapacity); + if (newCapacity > 0) { + // TODO: Implement primitive array pool. + mArray = Arrays.copyOf(mArray, newCapacity); + } + } + + public int getLength() { + return mLength; + } + + public void setLength(final int newLength) { + ensureCapacity(newLength); + mLength = newLength; + } + + public void reset(final int capacity) { + // TODO: Implement primitive array pool. + mArray = new int[capacity]; + mLength = 0; + } + + public int[] getPrimitiveArray() { + return mArray; + } + + public void set(final ResizableIntArray ip) { + // TODO: Implement primitive array pool. + mArray = ip.mArray; + mLength = ip.mLength; + } + + public void copy(final ResizableIntArray ip) { + final int newCapacity = calculateCapacity(ip.mLength); + if (newCapacity > 0) { + // TODO: Implement primitive array pool. + mArray = new int[newCapacity]; + } + System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); + mLength = ip.mLength; + } + + public void append(final ResizableIntArray src, final int startPos, final int length) { + if (length == 0) { + return; + } + final int currentLength = mLength; + final int newLength = currentLength + length; + ensureCapacity(newLength); + System.arraycopy(src.mArray, startPos, mArray, currentLength, length); + mLength = newLength; + } + + public void fill(final int value, final int startPos, final int length) { + if (startPos < 0 || length < 0) { + throw new IllegalArgumentException("startPos=" + startPos + "; length=" + length); + } + final int endPos = startPos + length; + ensureCapacity(endPos); + Arrays.fill(mArray, startPos, endPos, value); + if (mLength < endPos) { + mLength = endPos; + } + } +} diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 5786978a8..8b4c17322 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -28,6 +28,7 @@ import android.view.inputmethod.InputConnection; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import java.util.regex.Pattern; diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 70acdc771..45608f439 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -39,6 +39,7 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import com.android.inputmethodcommon.InputMethodSettingsFragment; public class Settings extends InputMethodSettingsFragment @@ -70,6 +71,7 @@ public class Settings extends InputMethodSettingsFragment "pref_key_preview_popup_dismiss_delay"; public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; + public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; public static final String PREF_KEYPRESS_SOUND_VOLUME = @@ -196,6 +198,12 @@ public class Settings extends InputMethodSettingsFragment textCorrectionGroup.removePreference(dictionaryLink); } + final boolean gestureInputEnabledByBuildConfig = res.getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + if (!gestureInputEnabledByBuildConfig) { + final Preference gestureInputPref = findPreference(PREF_GESTURE_INPUT); + miscSettings.removePreference(gestureInputPref); + } final boolean showUsabilityStudyModeOption = res.getBoolean(R.bool.config_enable_usability_study_mode_option) || ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS; @@ -208,7 +216,8 @@ public class Settings extends InputMethodSettingsFragment if (ProductionFlag.IS_EXPERIMENTAL) { if (usabilityStudyPref instanceof CheckBoxPreference) { CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; - checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true)); + checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, + ResearchLogger.DEFAULT_USABILITY_STUDY_MODE)); checkbox.setSummary(R.string.settings_warning_researcher_mode); } } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 10025daf8..3ed981375 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -83,6 +83,7 @@ public class SettingsValues { @SuppressWarnings("unused") // TODO: Use this private final float mKeypressSoundVolumeRawValue; private final InputMethodSubtype[] mAdditionalSubtypes; + public final boolean mGestureInputEnabled; // From the input box private final InputAttributes mInputAttributes; @@ -169,6 +170,10 @@ public class SettingsValues { mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray( getPrefAdditionalSubtypes(prefs, res)); + final boolean gestureInputEnabledByBuildConfig = res.getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + mGestureInputEnabled = gestureInputEnabledByBuildConfig + && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true); mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; mSuggestionVisibility = createSuggestionVisibility(res); } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 664de6774..a7a5fcb5f 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -98,9 +98,9 @@ public class SubtypeSwitcher { mConnectivityManager = (ConnectivityManager) service.getSystemService( Context.CONNECTIVITY_SERVICE); mCurrentSystemLocale = mResources.getConfiguration().locale; - mCurrentSubtype = mImm.getCurrentInputMethodSubtype(); mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); + mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype); if (mNoLanguageSubtype == null) { throw new RuntimeException("Can't find no lanugage with QWERTY subtype"); } @@ -113,7 +113,7 @@ public class SubtypeSwitcher { // Only configuration changed event is allowed to call this because this is heavy. private void updateAllParameters() { mCurrentSystemLocale = mResources.getConfiguration().locale; - updateSubtype(mImm.getCurrentInputMethodSubtype()); + updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); updateParametersOnStartInputView(); } @@ -142,7 +142,7 @@ public class SubtypeSwitcher { + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue()); Log.w(TAG, "Last subtype was disabled. Update to the current one."); } - updateSubtype(mImm.getCurrentInputMethodSubtype()); + updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); } } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index ca9dbaf05..6d346d179 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -33,7 +33,7 @@ public class WordComposer { private static final int N = BinaryDictionary.MAX_WORD_LENGTH; private int[] mPrimaryKeyCodes; - private final InputPointers mInputPointers = new InputPointers(); + private final InputPointers mInputPointers = new InputPointers(N); private final StringBuilder mTypedWord; private CharSequence mAutoCorrection; private boolean mIsResumed; diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index c6fe43b69..58b01aa55 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -42,10 +42,10 @@ public class MoreSuggestions extends Keyboard { private int mToPos; public static class MoreSuggestionsParam extends Keyboard.Params { - private final int[] mWidths = new int[SuggestionsView.MAX_SUGGESTIONS]; - private final int[] mRowNumbers = new int[SuggestionsView.MAX_SUGGESTIONS]; - private final int[] mColumnOrders = new int[SuggestionsView.MAX_SUGGESTIONS]; - private final int[] mNumColumnsInRow = new int[SuggestionsView.MAX_SUGGESTIONS]; + private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS]; + private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS]; + private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS]; + private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS]; private static final int MAX_COLUMNS_IN_ROW = 3; private int mNumRows; public Drawable mDivider; @@ -63,7 +63,7 @@ public class MoreSuggestions extends Keyboard { int row = 0; int pos = fromPos, rowStartPos = fromPos; - final int size = Math.min(suggestions.size(), SuggestionsView.MAX_SUGGESTIONS); + final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS); while (pos < size) { final String word = suggestions.getWord(pos).toString(); // TODO: Should take care of text x-scaling. diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java index 19287e3f3..5b23d7f3c 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java @@ -68,7 +68,7 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { @Override public void onCodeInput(int primaryCode, int x, int y) { final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE; - if (index >= 0 && index < SuggestionsView.MAX_SUGGESTIONS) { + if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) { mListener.onCustomRequest(index); } } diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 4d33f4ba5..b57ffd2de 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -60,15 +60,15 @@ import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; -public class SuggestionsView extends RelativeLayout implements OnClickListener, +public class SuggestionStripView extends RelativeLayout implements OnClickListener, OnLongClickListener { public interface Listener { public boolean addWordToUserDictionary(String word); @@ -98,24 +98,24 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private Listener mListener; private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; - private final SuggestionsViewParams mParams; + private final SuggestionStripViewParams mParams; private static final float MIN_TEXT_XSCALE = 0.70f; private final UiHandler mHandler = new UiHandler(this); - private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> { + private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> { private static final int MSG_HIDE_PREVIEW = 0; - public UiHandler(SuggestionsView outerInstance) { + public UiHandler(SuggestionStripView outerInstance) { super(outerInstance); } @Override public void dispatchMessage(Message msg) { - final SuggestionsView suggestionsView = getOuterInstance(); + final SuggestionStripView suggestionStripView = getOuterInstance(); switch (msg.what) { case MSG_HIDE_PREVIEW: - suggestionsView.hidePreview(); + suggestionStripView.hidePreview(); break; } } @@ -129,7 +129,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, } } - private static class SuggestionsViewParams { + private static class SuggestionStripViewParams { private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; private static final int DEFAULT_CENTER_SUGGESTION_PERCENTILE = 40; private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; @@ -175,7 +175,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private final TextView mLeftwardsArrowView; private final TextView mHintToSaveView; - public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle, + public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle, ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) { mWords = words; mDividers = dividers; @@ -191,38 +191,39 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final Resources res = word.getResources(); mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height); - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle); - mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0); + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle); + mSuggestionStripOption = a.getInt( + R.styleable.SuggestionStripView_suggestionStripOption, 0); final float alphaValidTypedWord = getPercent(a, - R.styleable.SuggestionsView_alphaValidTypedWord, 100); + R.styleable.SuggestionStripView_alphaValidTypedWord, 100); final float alphaTypedWord = getPercent(a, - R.styleable.SuggestionsView_alphaTypedWord, 100); + R.styleable.SuggestionStripView_alphaTypedWord, 100); final float alphaAutoCorrect = getPercent(a, - R.styleable.SuggestionsView_alphaAutoCorrect, 100); + R.styleable.SuggestionStripView_alphaAutoCorrect, 100); final float alphaSuggested = getPercent(a, - R.styleable.SuggestionsView_alphaSuggested, 100); - mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100); - mColorValidTypedWord = applyAlpha( - a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0), - alphaValidTypedWord); - mColorTypedWord = applyAlpha( - a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord); - mColorAutoCorrect = applyAlpha( - a.getColor(R.styleable.SuggestionsView_colorAutoCorrect, 0), alphaAutoCorrect); - mColorSuggested = applyAlpha( - a.getColor(R.styleable.SuggestionsView_colorSuggested, 0), alphaSuggested); + R.styleable.SuggestionStripView_alphaSuggested, 100); + mAlphaObsoleted = getPercent(a, + R.styleable.SuggestionStripView_alphaSuggested, 100); + 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.SuggestionsView_suggestionsCountInStrip, + R.styleable.SuggestionStripView_suggestionsCountInStrip, DEFAULT_SUGGESTIONS_COUNT_IN_STRIP); mCenterSuggestionWeight = getPercent(a, - R.styleable.SuggestionsView_centerSuggestionPercentile, + R.styleable.SuggestionStripView_centerSuggestionPercentile, DEFAULT_CENTER_SUGGESTION_PERCENTILE); mMaxMoreSuggestionsRow = a.getInt( - R.styleable.SuggestionsView_maxMoreSuggestionsRow, + R.styleable.SuggestionStripView_maxMoreSuggestionsRow, DEFAULT_MAX_MORE_SUGGESTIONS_ROW); mMinMoreSuggestionsWidth = getRatio(a, - R.styleable.SuggestionsView_minMoreSuggestionsWidth); + R.styleable.SuggestionStripView_minMoreSuggestionsWidth); a.recycle(); mMoreSuggestionsHint = getMoreSuggestionsHint(res, @@ -596,15 +597,15 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, } /** - * Construct a {@link SuggestionsView} for showing suggestions to be picked by the user. + * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user. * @param context * @param attrs */ - public SuggestionsView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.suggestionsViewStyle); + public SuggestionStripView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.suggestionStripViewStyle); } - public SuggestionsView(Context context, AttributeSet attrs, int defStyle) { + public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final LayoutInflater inflater = LayoutInflater.from(context); @@ -631,7 +632,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null)); } - mParams = new SuggestionsViewParams(context, attrs, defStyle, mWords, mDividers, mInfos); + mParams = new SuggestionStripViewParams( + context, attrs, defStyle, mWords, mDividers, mInfos); mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null); mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer @@ -677,7 +679,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mSuggestedWords = suggestedWords; mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth()); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.suggestionsView_setSuggestions(mSuggestedWords); + ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords); } } @@ -759,7 +761,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, } private boolean showMoreSuggestions() { - final SuggestionsViewParams params = mParams; + final SuggestionStripViewParams params = mParams; if (params.mMoreSuggestionsAvailable) { final int stripWidth = getWidth(); final View container = mMoreSuggestionsContainer; |