diff options
Diffstat (limited to 'java/src')
16 files changed, 230 insertions, 200 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 6a01b0190..ea86d98cb 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -209,7 +209,7 @@ public final class KeyCodeDescriptionMapper { private String getDescriptionForActionKey(final Context context, final Keyboard keyboard, final Key key) { final KeyboardId keyboardId = keyboard.mId; - final int actionId = keyboardId.imeActionId(); + final int actionId = keyboardId.imeAction(); final int resId; // Always use the label, if available. diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index f9ff7b089..02116ca08 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -172,12 +172,7 @@ public final class KeyboardId { } public int imeAction() { - return InputTypeUtils.getActionIdFromEditorInfo(mEditorInfo); - } - - public int imeActionId() { - final int actionId = imeAction(); - return actionId == InputTypeUtils.IME_ACTION_CUSTOM_LABEL ? mEditorInfo.actionId : actionId; + return InputTypeUtils.getImeOptionsActionIdFromEditorInfo(mEditorInfo); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java index a8407254f..4a8407cb5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java @@ -138,6 +138,7 @@ final class GesturePreviewTrail { } private final RoundedLine mRoundedLine = new RoundedLine(); + private final Rect mRoundedLineBounds = new Rect(); /** * Draw gesture preview trail @@ -149,6 +150,8 @@ final class GesturePreviewTrail { */ public boolean drawGestureTrail(final Canvas canvas, final Paint paint, final Rect outBoundsRect, final Params params) { + // Initialize bounds rectangle. + outBoundsRect.setEmpty(); final int trailSize = mEventTimes.getLength(); if (trailSize == 0) { return false; @@ -171,39 +174,32 @@ final class GesturePreviewTrail { if (startIndex < trailSize) { paint.setColor(params.mTrailColor); paint.setStyle(Paint.Style.FILL); - final RoundedLine line = mRoundedLine; + final RoundedLine roundedLine = mRoundedLine; int p1x = getXCoordValue(xCoords[startIndex]); int p1y = yCoords[startIndex]; final int lastTime = sinceDown - eventTimes[startIndex]; - float maxWidth = getWidth(lastTime, params); - float r1 = maxWidth / 2.0f; - // Initialize bounds rectangle. - outBoundsRect.set(p1x, p1y, p1x, p1y); + float r1 = getWidth(lastTime, params) / 2.0f; for (int i = startIndex + 1; i < trailSize; i++) { final int elapsedTime = sinceDown - eventTimes[i]; final int p2x = getXCoordValue(xCoords[i]); final int p2y = yCoords[i]; - final float width = getWidth(elapsedTime, params); - final float r2 = width / 2.0f; + final float r2 = getWidth(elapsedTime, params) / 2.0f; // Draw trail line only when the current point isn't a down point. if (!isDownEventXCoord(xCoords[i])) { - final Path path = line.makePath(p1x, p1y, r1, p2x, p2y, r2); + final Path path = roundedLine.makePath(p1x, p1y, r1, p2x, p2y, r2); if (path != null) { final int alpha = getAlpha(elapsedTime, params); paint.setAlpha(alpha); canvas.drawPath(path, paint); // Take union for the bounds. - outBoundsRect.union(p2x, p2y); - maxWidth = Math.max(maxWidth, width); + roundedLine.getBounds(mRoundedLineBounds); + outBoundsRect.union(mRoundedLineBounds); } } p1x = p2x; p1y = p2y; r1 = r2; } - // Take care of trail line width. - final int inset = -((int)maxWidth + 1); - outBoundsRect.inset(inset, inset); } final int newSize = trailSize - startIndex; diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index bfb7b1fe0..7c87467bb 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -52,7 +52,8 @@ public final class PreviewPlacerView extends RelativeLayout { private int mOffscreenOffsetY; private Bitmap mOffscreenBuffer; private final Canvas mOffscreenCanvas = new Canvas(); - private final Rect mOffscreenDirtyRect = new Rect(); + private final Rect mOffscreenSrcRect = new Rect(); + private final Rect mDirtyRect = new Rect(); private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail private final GestureFloatingPreviewText mGestureFloatingPreviewText; private boolean mShowSlidingKeyInputPreview; @@ -193,6 +194,7 @@ public final class PreviewPlacerView extends RelativeLayout { mOffscreenBuffer = Bitmap.createBitmap( mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888); mOffscreenCanvas.setBitmap(mOffscreenBuffer); + mOffscreenCanvas.translate(0, mOffscreenOffsetY); } @Override @@ -205,19 +207,18 @@ public final class PreviewPlacerView extends RelativeLayout { mayAllocateOffscreenBuffer(); // Draw gesture trails to offscreen buffer. final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails( - mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect); + mOffscreenCanvas, mGesturePaint, mDirtyRect); + if (needsUpdatingGesturePreviewTrail) { + mDrawingHandler.postUpdateGestureTrailPreview(); + } // Transfer offscreen buffer to screen. - if (!mOffscreenDirtyRect.isEmpty()) { - canvas.translate(0, - mOffscreenOffsetY); - canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect, - mGesturePaint); - canvas.translate(0, mOffscreenOffsetY); + if (!mDirtyRect.isEmpty()) { + mOffscreenSrcRect.set(mDirtyRect); + mOffscreenSrcRect.offset(0, mOffscreenOffsetY); + canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null); // Note: Defer clearing the dirty rectangle here because we will get cleared // rectangle on the canvas. } - if (needsUpdatingGesturePreviewTrail) { - mDrawingHandler.postUpdateGestureTrailPreview(); - } } mGestureFloatingPreviewText.onDraw(canvas); if (mShowSlidingKeyInputPreview) { @@ -235,10 +236,8 @@ public final class PreviewPlacerView extends RelativeLayout { offscreenCanvas.drawRect(dirtyRect, paint); } dirtyRect.setEmpty(); - - // Draw gesture trails to offscreen buffer. - offscreenCanvas.translate(0, mOffscreenOffsetY); boolean needsUpdatingGesturePreviewTrail = false; + // Draw gesture trails to offscreen buffer. synchronized (mGesturePreviewTrails) { // Trails count == fingers count that have ever been active. final int trailsCount = mGesturePreviewTrails.size(); @@ -251,20 +250,9 @@ public final class PreviewPlacerView extends RelativeLayout { dirtyRect.union(mGesturePreviewTrailBoundsRect); } } - offscreenCanvas.translate(0, -mOffscreenOffsetY); - - // Clip dirty rectangle with offscreen buffer width/height. - dirtyRect.offset(0, mOffscreenOffsetY); - clipRect(dirtyRect, 0, 0, mOffscreenWidth, mOffscreenHeight); return needsUpdatingGesturePreviewTrail; } - private static void clipRect(final Rect out, final int left, final int top, final int right, - final int bottom) { - out.set(Math.max(out.left, left), Math.max(out.top, top), Math.min(out.right, right), - Math.min(out.bottom, bottom)); - } - public void setGestureFloatingPreviewText(final SuggestedWords suggestedWords) { if (!mGestureFloatingPreviewText.isPreviewEnabled()) return; mGestureFloatingPreviewText.setSuggetedWords(suggestedWords); diff --git a/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java b/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java index 1f5252077..cd6efc4b7 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java +++ b/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java @@ -15,6 +15,7 @@ package com.android.inputmethod.keyboard.internal; import android.graphics.Path; +import android.graphics.Rect; import android.graphics.RectF; public final class RoundedLine { @@ -100,4 +101,10 @@ public final class RoundedLine { mPath.close(); return mPath; } + + public void getBounds(final Rect outBounds) { + // Reuse mArc1 as working variable + mPath.computeBounds(mArc1, true /* unused */); + mArc1.roundOut(outBounds); + } } diff --git a/java/src/com/android/inputmethod/latin/InputTypeUtils.java b/java/src/com/android/inputmethod/latin/InputTypeUtils.java index 55414b809..e2eacb3f0 100644 --- a/java/src/com/android/inputmethod/latin/InputTypeUtils.java +++ b/java/src/com/android/inputmethod/latin/InputTypeUtils.java @@ -105,7 +105,7 @@ public final class InputTypeUtils implements InputType { return true; } - public static int getActionIdFromEditorInfo(final EditorInfo editorInfo) { + public static int getImeOptionsActionIdFromEditorInfo(final EditorInfo editorInfo) { final int actionId = editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION; if ((editorInfo.imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { return EditorInfo.IME_ACTION_NONE; @@ -115,4 +115,9 @@ public final class InputTypeUtils implements InputType { return actionId; } } + + public static int getConcreteActionIdFromEditorInfo(final EditorInfo editorInfo) { + final int actionId = getImeOptionsActionIdFromEditorInfo(editorInfo); + return actionId == InputTypeUtils.IME_ACTION_CUSTOM_LABEL ? editorInfo.actionId : actionId; + } } diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 488a6fcf2..a4019e906 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -45,19 +45,21 @@ public final class LastComposedWord { public final String mCommittedWord; public final String mSeparatorString; public final String mPrevWord; + public final int mCapitalizedMode; public final InputPointers mInputPointers = new InputPointers(Constants.Dictionary.MAX_WORD_LENGTH); private boolean mActive; public static final LastComposedWord NOT_A_COMPOSED_WORD = - new LastComposedWord(null, null, "", "", NOT_A_SEPARATOR, null); + new LastComposedWord(null, null, "", "", NOT_A_SEPARATOR, null, + WordComposer.CAPS_MODE_OFF); // Warning: this is using the passed objects as is and fully expects them to be // immutable. Do not fiddle with their contents after you passed them to this constructor. public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers, - final String typedWord, final String committedWord, - final String separatorString, final String prevWord) { + final String typedWord, final String committedWord, final String separatorString, + final String prevWord, final int capitalizedMode) { mPrimaryKeyCodes = primaryKeyCodes; if (inputPointers != null) { mInputPointers.copy(inputPointers); @@ -67,6 +69,7 @@ public final class LastComposedWord { mSeparatorString = separatorString; mActive = true; mPrevWord = prevWord; + mCapitalizedMode = capitalizedMode; } public void deactivate() { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ab21cff47..d02c4df7e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -166,6 +166,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private boolean mExpectingUpdateSelection; private int mDeleteCount; private long mLastKeyTime; + private int mActionId; // Member variables for remembering the current device orientation. private int mDisplayOrientation; @@ -754,6 +755,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mLastSelectionStart = editorInfo.initialSelStart; mLastSelectionEnd = editorInfo.initialSelEnd; + mActionId = InputTypeUtils.getConcreteActionIdFromEditorInfo(editorInfo); mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacePeriodTimer(); @@ -1223,7 +1225,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mPositionalInfoForUserDictPendingAddition = new PositionalInfoForUserDictPendingAddition( - word, mLastSelectionEnd, getCurrentInputEditorInfo()); + word, mLastSelectionEnd, getCurrentInputEditorInfo(), + mLastComposedWord.mCapitalizedMode); mUserDictionary.addWordToUserDictionary(word, 128); } @@ -1272,10 +1275,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction return mOptionsDialog != null && mOptionsDialog.isShowing(); } - private static int getActionId(final Keyboard keyboard) { - return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE; - } - private void performEditorAction(final int actionId) { mConnection.performEditorAction(actionId); } @@ -1384,10 +1383,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } break; case Constants.CODE_ACTION_ENTER: - final int actionId = getActionId(switcher.getKeyboard()); - if (EditorInfo.IME_ACTION_NONE != actionId - && EditorInfo.IME_ACTION_UNSPECIFIED != actionId) { - performEditorAction(actionId); + if (EditorInfo.IME_ACTION_NONE != mActionId + && EditorInfo.IME_ACTION_UNSPECIFIED != mActionId) { + performEditorAction(mActionId); break; } didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState); @@ -1496,12 +1494,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mSpaceState = SPACE_STATE_PHANTOM; } else { final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); - // TODO: reverse this logic. We should have the means to determine whether a character - // should usually be followed by a space, and it should be more readable. - if (Constants.NOT_A_CODE != codePointBeforeCursor - && !Character.isWhitespace(codePointBeforeCursor) - && !mSettings.getCurrent().isPhantomSpacePromotingSymbol(codePointBeforeCursor) - && !mSettings.getCurrent().isWeakSpaceStripper(codePointBeforeCursor)) { + if (mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) { mSpaceState = SPACE_STATE_PHANTOM; } } @@ -1778,25 +1771,22 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } } + /* + * Strip a trailing space if necessary and returns whether it's a swap weak space situation. + */ private boolean maybeStripSpace(final int code, final int spaceState, final boolean isFromSuggestionStrip) { if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { mConnection.removeTrailingSpace(); return false; - } else if ((SPACE_STATE_WEAK == spaceState - || SPACE_STATE_SWAP_PUNCTUATION == spaceState) + } + if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) && isFromSuggestionStrip) { - if (mSettings.getCurrent().isWeakSpaceSwapper(code)) { - return true; - } else { - if (mSettings.getCurrent().isWeakSpaceStripper(code)) { - mConnection.removeTrailingSpace(); - } - return false; - } - } else { - return false; + if (mSettings.getCurrent().isUsuallyPrecededBySpace(code)) return false; + if (mSettings.getCurrent().isUsuallyFollowedBySpace(code)) return true; + mConnection.removeTrailingSpace(); } + return false; } private void handleCharacter(final int primaryCode, final int x, @@ -1804,7 +1794,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction boolean isComposingWord = mWordComposer.isComposingWord(); if (SPACE_STATE_PHANTOM == spaceState && - !mSettings.getCurrent().isSymbolExcludedFromWordSeparators(primaryCode)) { + !mSettings.getCurrent().isWordConnector(primaryCode)) { if (isComposingWord) { // Sanity check throw new RuntimeException("Should not be composing here"); @@ -1816,7 +1806,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI // thread here. if (!isComposingWord && (isAlphabet(primaryCode) - || mSettings.getCurrent().isSymbolExcludedFromWordSeparators(primaryCode)) + || mSettings.getCurrent().isWordConnector(primaryCode)) && mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation) && !mConnection.isCursorTouchingWord(mSettings.getCurrent())) { // Reset entirely the composing state anyway, then start composing a new word unless @@ -1870,7 +1860,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private boolean handleSeparator(final int primaryCode, final int x, final int y, final int spaceState) { if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_handleSeparator(); + ResearchLogger.recordTimeForLogUnitSplit(); } boolean didAutoCorrect = false; // Handle separator @@ -1888,7 +1878,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction Constants.SUGGESTION_STRIP_COORDINATE == x); if (SPACE_STATE_PHANTOM == spaceState && - mSettings.getCurrent().isPhantomSpacePromotingSymbol(primaryCode)) { + mSettings.getCurrent().isUsuallyPrecededBySpace(primaryCode)) { promotePhantomSpace(); } sendKeyCodePoint(primaryCode); @@ -1903,16 +1893,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mHandler.startDoubleSpacePeriodTimer(); - if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) { - mHandler.postUpdateSuggestionStrip(); - } + mHandler.postUpdateSuggestionStrip(); } else { if (swapWeakSpace) { swapSwapperAndSpace(); mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; } else if (SPACE_STATE_PHANTOM == spaceState - && !mSettings.getCurrent().isWeakSpaceStripper(primaryCode) - && !mSettings.getCurrent().isPhantomSpacePromotingSymbol(primaryCode)) { + && mSettings.getCurrent().isUsuallyFollowedBySpace(primaryCode)) { // If we are in phantom space state, and the user presses a separator, we want to // stay in phantom space state so that the next keypress has a chance to add the // space. For example, if I type "Good dat", pick "day" from the suggestion strip @@ -2161,9 +2148,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // In the batch input mode, a manually picked suggested word should just replace // the current batch input text and there is no need for a phantom space. && !mWordComposer.isBatchMode()) { - int firstChar = Character.codePointAt(suggestion, 0); - if ((!mSettings.getCurrent().isWeakSpaceStripper(firstChar)) - && (!mSettings.getCurrent().isWeakSpaceSwapper(firstChar))) { + final int firstChar = Character.codePointAt(suggestion, 0); + if (!mSettings.getCurrent().isWordSeparator(firstChar) + || mSettings.getCurrent().isUsuallyPrecededBySpace(firstChar)) { promotePhantomSpace(); } } diff --git a/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java b/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java index 1fd25636c..a33cefcd6 100644 --- a/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java +++ b/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java @@ -33,13 +33,15 @@ public final class PositionalInfoForUserDictPendingAddition { final private String mOriginalWord; final private int mCursorPos; // Position of the cursor after the word final private EditorInfo mEditorInfo; // On what binding this has been added + final private int mCapitalizedMode; private String mActualWordBeingAdded; public PositionalInfoForUserDictPendingAddition(final String word, final int cursorPos, - final EditorInfo editorInfo) { + final EditorInfo editorInfo, final int capitalizedMode) { mOriginalWord = word; mCursorPos = cursorPos; mEditorInfo = editorInfo; + mCapitalizedMode = capitalizedMode; } public void setActualWordBeingAdded(final String actualWordBeingAdded) { diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index f7268fc33..0e75533f5 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -577,11 +577,11 @@ public final class RichInputConnection { final CharSequence before = getTextBeforeCursor(1, 0); final CharSequence after = getTextAfterCursor(1, 0); if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0)) - && !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) { + && !settingsValues.isWordConnector(before.charAt(0))) { return true; } if (!TextUtils.isEmpty(after) && !settingsValues.isWordSeparator(after.charAt(0)) - && !settingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) { + && !settingsValues.isWordConnector(after.charAt(0))) { return true; } return false; @@ -633,12 +633,9 @@ public final class RichInputConnection { final char firstChar = word.charAt(0); // we just tested that word is not empty if (word.length() == 1 && !Character.isLetter(firstChar)) return null; - // We only suggest on words that start with a letter or a symbol that is excluded from - // word separators (see #handleCharacterWhileInBatchEdit). - if (!(Character.isLetter(firstChar) - || settings.isSymbolExcludedFromWordSeparators(firstChar))) { - return null; - } + // We don't restart suggestion if the first character is not a letter, because we don't + // start composing when the first character is not a letter. + if (!Character.isLetter(firstChar)) return null; return word; } diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialog.java b/java/src/com/android/inputmethod/latin/SeekBarDialog.java index e576c0984..c736d1b1a 100644 --- a/java/src/com/android/inputmethod/latin/SeekBarDialog.java +++ b/java/src/com/android/inputmethod/latin/SeekBarDialog.java @@ -30,6 +30,8 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, public interface Listener { public void onPositiveButtonClick(final SeekBarDialog dialog); public void onNegativeButtonClick(final SeekBarDialog dialog); + public void onNeutralButtonClick(final SeekBarDialog dialog); + public void onDismiss(final SeekBarDialog dialog); public void onProgressChanged(final SeekBarDialog dialog); public void onStartTrackingTouch(final SeekBarDialog dialog); public void onStopTrackingTouch(final SeekBarDialog dialog); @@ -39,7 +41,11 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, @Override public void onPositiveButtonClick(final SeekBarDialog dialog) {} @Override - public void onNegativeButtonClick(final SeekBarDialog dialog) { dialog.dismiss(); } + public void onNegativeButtonClick(final SeekBarDialog dialog) {} + @Override + public void onNeutralButtonClick(final SeekBarDialog dialog) {} + @Override + public void onDismiss(final SeekBarDialog dialog) {} @Override public void onProgressChanged(final SeekBarDialog dialog) {} @Override @@ -63,6 +69,9 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, dialogBuilder.setView(builder.mView); dialogBuilder.setPositiveButton(android.R.string.ok, this); dialogBuilder.setNegativeButton(android.R.string.cancel, this); + if (builder.mNeutralButtonTextResId != 0) { + dialogBuilder.setNeutralButton(builder.mNeutralButtonTextResId, this); + } mDialog = dialogBuilder.create(); mListener = (builder.mListener == null) ? EMPTY_ADAPTER : builder.mListener; mValueView = (TextView)builder.mView.findViewById(R.id.seek_bar_dialog_value); @@ -101,15 +110,21 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, } @Override - public void onClick(final DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_POSITIVE) { + public void onClick(final DialogInterface dialog, final int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: mListener.onPositiveButtonClick(this); - return; - } - if (which == DialogInterface.BUTTON_NEGATIVE) { + break; + case DialogInterface.BUTTON_NEGATIVE: mListener.onNegativeButtonClick(this); + break; + case DialogInterface.BUTTON_NEUTRAL: + mListener.onNeutralButtonClick(this); + break; + default: return; } + mListener.onDismiss(this); } @Override @@ -135,6 +150,7 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, final AlertDialog.Builder mDialogBuilder; final View mView; + int mNeutralButtonTextResId; int mMaxValue; int mValueFormatResId; int mValue; @@ -150,8 +166,14 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, return this; } + public Builder setNeutralButtonText(final int resId) { + mNeutralButtonTextResId = resId; + return this; + } + public Builder setMaxValue(final int max) { mMaxValue = max; + mValue = Math.min(mValue, max); return this; } @@ -161,7 +183,7 @@ public final class SeekBarDialog implements DialogInterface.OnClickListener, } public Builder setValue(final int value) { - mValue = value; + mValue = Math.min(value, mMaxValue); return this; } diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index c5930a935..866bef0f2 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -167,19 +167,21 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static float readKeypressSoundVolume(final SharedPreferences prefs, final Resources res) { final float volume = prefs.getFloat(PREF_KEYPRESS_SOUND_VOLUME, -1.0f); - if (volume >= 0) { - return volume; - } + return (volume >= 0) ? volume : readDefaultKeypressSoundVolume(res); + } + + public static float readDefaultKeypressSoundVolume(final Resources res) { return Float.parseFloat( ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_volumes)); } - public static int readVibrationDuration(final SharedPreferences prefs, + public static int readKeypressVibrationDuration(final SharedPreferences prefs, final Resources res) { final int ms = prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, -1); - if (ms >= 0) { - return ms; - } + return (ms >= 0) ? ms : readDefaultKeypressVibrationDuration(res); + } + + public static int readDefaultKeypressVibrationDuration(final Resources res) { return Integer.parseInt( ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations)); } diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java index 507a37b7c..6a4371835 100644 --- a/java/src/com/android/inputmethod/latin/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java @@ -180,7 +180,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment }); mKeypressVibrationDurationSettingsPref.setSummary( res.getString(R.string.settings_keypress_vibration_duration, - Settings.readVibrationDuration(prefs, res))); + Settings.readKeypressVibrationDuration(prefs, res))); } mKeypressSoundVolumeSettingsPref = @@ -308,10 +308,29 @@ public final class SettingsFragment extends InputMethodSettingsFragment final Context context = getActivity(); final PreferenceScreen settingsPref = mKeypressVibrationDurationSettingsPref; final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() { + private void writePreference(final SharedPreferences sp, final int value) { + sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, value).apply(); + } + + private void feedbackSettingsValue(final int value) { + AudioAndHapticFeedbackManager.getInstance().vibrate(value); + } + @Override public void onPositiveButtonClick(final SeekBarDialog dialog) { - final int ms = dialog.getValue(); - sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); + writePreference(sp, dialog.getValue()); + } + + @Override + public void onNeutralButtonClick(final SeekBarDialog dialog) { + final int defaultValue = + Settings.readDefaultKeypressVibrationDuration(context.getResources()); + dialog.setValue(defaultValue, false /* fromUser */); + writePreference(sp, defaultValue); + } + + @Override + public void onDismiss(final SeekBarDialog dialog) { if (settingsPref != null) { settingsPref.setSummary(dialog.getValueText()); } @@ -319,13 +338,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment @Override public void onStopTrackingTouch(final SeekBarDialog dialog) { - final int ms = dialog.getValue(); - AudioAndHapticFeedbackManager.getInstance().vibrate(ms); + feedbackSettingsValue(dialog.getValue()); } }; - final int currentMs = Settings.readVibrationDuration(sp, getResources()); + final int currentMs = Settings.readKeypressVibrationDuration(sp, getResources()); final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context); builder.setTitle(R.string.prefs_keypress_vibration_duration_settings) + .setNeutralButtonText(R.string.button_default) .setListener(listener) .setMaxValue(AudioAndHapticFeedbackManager.MAX_KEYPRESS_VIBRATION_DURATION) .setValueFromat(R.string.settings_keypress_vibration_duration) @@ -348,10 +367,29 @@ public final class SettingsFragment extends InputMethodSettingsFragment final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); final PreferenceScreen settingsPref = mKeypressSoundVolumeSettingsPref; final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() { + private void writePreference(final SharedPreferences sp, final float value) { + sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, value).apply(); + } + + private void feedbackSettingsValue(final float value) { + am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, value); + } + @Override public void onPositiveButtonClick(final SeekBarDialog dialog) { - final float volume = dialog.getValue() / PERCENT_FLOAT; - sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply(); + writePreference(sp, dialog.getValue() / PERCENT_FLOAT); + } + + @Override + public void onNeutralButtonClick(final SeekBarDialog dialog) { + final float defaultValue = + Settings.readDefaultKeypressSoundVolume(context.getResources()); + dialog.setValue((int)(defaultValue * PERCENT_INT), false /* fromUser */); + writePreference(sp, defaultValue); + } + + @Override + public void onDismiss(final SeekBarDialog dialog) { if (settingsPref != null) { settingsPref.setSummary(dialog.getValueText()); } @@ -359,13 +397,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment @Override public void onStopTrackingTouch(final SeekBarDialog dialog) { - final float volume = dialog.getValue() / PERCENT_FLOAT; - am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, volume); + feedbackSettingsValue(dialog.getValue() / PERCENT_FLOAT); } }; final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context); final int currentVolumeInt = getCurrentKeyPressSoundVolumePercent(sp, getResources()); builder.setTitle(R.string.prefs_keypress_sound_volume_settings) + .setNeutralButtonText(R.string.button_default) .setListener(listener) .setMaxValue(PERCENT_INT) .setValue(currentVolumeInt) diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 9a2024618..1e3bdf0fd 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -37,11 +37,10 @@ public final class SettingsValues { // From resources: public final int mDelayUpdateOldSuggestions; - public final String mWeakSpaceStrippers; - public final String mWeakSpaceSwappers; - private final String mPhantomSpacePromotingSymbols; + public final int[] mSymbolsPrecededBySpace; + public final int[] mSymbolsFollowedBySpace; + public final int[] mWordConnectors; public final SuggestedWords mSuggestPuncList; - private final String mSymbolsExcludedFromWordSeparators; public final String mWordSeparators; public final CharSequence mHintToSaveText; @@ -79,25 +78,19 @@ public final class SettingsValues { final InputAttributes inputAttributes) { // Get the resources mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); - mWeakSpaceStrippers = res.getString(R.string.weak_space_stripping_symbols); - mWeakSpaceSwappers = res.getString(R.string.weak_space_swapping_symbols); - mPhantomSpacePromotingSymbols = res.getString(R.string.phantom_space_promoting_symbols); - if (LatinImeLogger.sDBG) { - final int length = mWeakSpaceStrippers.length(); - for (int i = 0; i < length; i = mWeakSpaceStrippers.offsetByCodePoints(i, 1)) { - if (isWeakSpaceSwapper(mWeakSpaceStrippers.codePointAt(i))) { - throw new RuntimeException("Char code " + mWeakSpaceStrippers.codePointAt(i) - + " is both a weak space swapper and stripper."); - } - } - } + mSymbolsPrecededBySpace = + StringUtils.toCodePointArray(res.getString(R.string.symbols_preceded_by_space)); + Arrays.sort(mSymbolsPrecededBySpace); + mSymbolsFollowedBySpace = + StringUtils.toCodePointArray(res.getString(R.string.symbols_followed_by_space)); + Arrays.sort(mSymbolsFollowedBySpace); + mWordConnectors = + StringUtils.toCodePointArray(res.getString(R.string.symbols_word_connectors)); + Arrays.sort(mWordConnectors); final String[] suggestPuncsSpec = KeySpecParser.parseCsvString( res.getString(R.string.suggested_punctuations), null); mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec); - mSymbolsExcludedFromWordSeparators = - res.getString(R.string.symbols_excluded_from_word_separators); - mWordSeparators = createWordSeparators(mWeakSpaceStrippers, mWeakSpaceSwappers, - mSymbolsExcludedFromWordSeparators, res); + mWordSeparators = res.getString(R.string.symbols_word_separators); mHintToSaveText = res.getText(R.string.hint_add_to_dictionary); // Store the input attributes @@ -128,7 +121,7 @@ public final class SettingsValues { mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); // Compute other readable settings - mKeypressVibrationDuration = Settings.readVibrationDuration(prefs, res); + mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res); mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res); mAutoCorrectionThreshold = readAutoCorrectionThreshold(res, @@ -169,25 +162,16 @@ public final class SettingsValues { return mWordSeparators.contains(String.valueOf((char)code)); } - public boolean isSymbolExcludedFromWordSeparators(final int code) { - return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code)); - } - - // TODO: use "Phantom" instead of "Weak" in this method name - public boolean isWeakSpaceStripper(final int code) { - // TODO: this does not work if the code does not fit in a char - return mWeakSpaceStrippers.contains(String.valueOf((char)code)); + public boolean isWordConnector(final int code) { + return Arrays.binarySearch(mWordConnectors, code) >= 0; } - // TODO: use "Phantom" instead of "Weak" in this method name - public boolean isWeakSpaceSwapper(final int code) { - // TODO: this does not work if the code does not fit in a char - return mWeakSpaceSwappers.contains(String.valueOf((char)code)); + public boolean isUsuallyPrecededBySpace(final int code) { + return Arrays.binarySearch(mSymbolsPrecededBySpace, code) >= 0; } - public boolean isPhantomSpacePromotingSymbol(final int code) { - // TODO: this does not work if the code does not fit in a char - return mPhantomSpacePromotingSymbols.contains(String.valueOf((char)code)); + public boolean isUsuallyFollowedBySpace(final int code) { + return Arrays.binarySearch(mSymbolsFollowedBySpace, code) >= 0; } public boolean shouldInsertSpacesAutomatically() { @@ -239,18 +223,6 @@ public final class SettingsValues { false /* isPrediction */); } - private static String createWordSeparators(final String weakSpaceStrippers, - final String weakSpaceSwappers, final String symbolsExcludedFromWordSeparators, - final Resources res) { - String wordSeparators = weakSpaceStrippers + weakSpaceSwappers - + res.getString(R.string.phantom_space_promoting_symbols); - for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) { - wordSeparators = wordSeparators.replace( - symbolsExcludedFromWordSeparators.substring(i, i + 1), ""); - } - return wordSeparators; - } - private static final int SUGGESTION_VISIBILITY_SHOW_VALUE = R.string.prefs_suggestion_visibility_show_value; private static final int SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE = diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 4f1759079..b9ec4979d 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -350,7 +350,7 @@ public final class WordComposer { mPrimaryKeyCodes = new int[MAX_WORD_LENGTH]; final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes, mInputPointers, mTypedWord.toString(), committedWord, separatorString, - prevWord); + prevWord, mCapitalizedMode); mInputPointers.reset(); if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) { @@ -374,6 +374,7 @@ public final class WordComposer { mTypedWord.setLength(0); mTypedWord.append(lastComposedWord.mTypedWord); refreshSize(); + mCapitalizedMode = lastComposedWord.mCapitalizedMode; mAutoCorrection = null; // This will be filled by the next call to updateSuggestion. mIsResumed = true; } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 8b6bff495..a2bcf4441 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -106,7 +106,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // Change the default indicator to something very visible. Currently two red vertical bars on // either side of they keyboard. private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false || IS_LOGGING_EVERYTHING; - public static final int FEEDBACK_WORD_BUFFER_SIZE = 5; + // FEEDBACK_WORD_BUFFER_SIZE should add 1 because it must also hold the feedback LogUnit itself. + public static final int FEEDBACK_WORD_BUFFER_SIZE = (Integer.MAX_VALUE - 1) + 1; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; @@ -391,9 +392,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } if (mFeedbackLogBuffer == null) { mFeedbackLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); - // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold - // the feedback LogUnit itself. - mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); + mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE); } } @@ -522,8 +521,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang */ private boolean mInFeedbackDialog = false; + + // The feedback dialog causes stop() to be called for the keyboard connected to the original + // window. This is because the feedback dialog must present its own EditText box that displays + // a keyboard. stop() normally causes mFeedbackLogBuffer, which contains the user's data, to be + // cleared, and causes mFeedbackLog, which is ready to collect information in case the user + // wants to upload, to be closed. This is good because we don't need to log information about + // what the user is typing in the feedback dialog, but bad because this data must be uploaded. + // Here we save the LogBuffer and Log so the feedback dialog can later access their data. + private LogBuffer mSavedFeedbackLogBuffer; + private ResearchLog mSavedFeedbackLog; + public void presentFeedbackDialog(LatinIME latinIME) { mInFeedbackDialog = true; + mSavedFeedbackLogBuffer = mFeedbackLogBuffer; + mSavedFeedbackLog = mFeedbackLog; + // Set the non-saved versions to null so that the stop() caused by switching to the + // Feedback dialog will not close them. + mFeedbackLogBuffer = null; + mFeedbackLog = null; latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class); } @@ -589,28 +605,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private static final LogStatement LOGSTATEMENT_FEEDBACK = - new LogStatement("UserTimestamp", false, false, "contents"); + new LogStatement("UserFeedback", false, false, "contents"); public void sendFeedback(final String feedbackContents, final boolean includeHistory) { - if (mFeedbackLogBuffer == null) { + if (mSavedFeedbackLogBuffer == null) { return; } - if (includeHistory) { - commitCurrentLogUnit(); - } else { - mFeedbackLogBuffer.clear(); + if (!includeHistory) { + mSavedFeedbackLogBuffer.clear(); } final LogUnit feedbackLogUnit = new LogUnit(); feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(), feedbackContents); mFeedbackLogBuffer.shiftIn(feedbackLogUnit); - publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); - mFeedbackLog.close(new Runnable() { + publishLogBuffer(mFeedbackLogBuffer, mSavedFeedbackLog, true /* isIncludingPrivateData */); + mSavedFeedbackLog.close(new Runnable() { @Override public void run() { uploadNow(); } }); - mFeedbackLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); } public void uploadNow() { @@ -643,13 +656,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - if (DEBUG) { - Log.d(TAG, "iatl: " + - "mipw=" + mIsPasswordView + - ", mils=" + mIsLoggingSuspended + - ", sil=" + sIsLogging + - ", mInFeedbackDialog=" + mInFeedbackDialog); - } return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog; } @@ -852,6 +858,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit = newLogUnit; } + /** + * Record the time of a MotionEvent.ACTION_DOWN. + * + * Warning: Not thread safe. Only call from the main thread. + */ private void setSavedDownEventTime(final long time) { mSavedDownEventTime = time; } @@ -1475,20 +1486,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private boolean isExpectingCommitText = false; /** - * Log a call to RichInputConnection.commitPartialText + * Log a call to (UnknownClass).commitPartialText * * SystemResponse: The IME is committing part of a word. This happens if a space is * automatically inserted to split a single typed string into two or more words. */ // TODO: This method is currently unused. Find where it should be called from in the IME and // add invocations. - private static final LogStatement LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT = - new LogStatement("LatinIMECommitPartialText", true, false, "newCursorPosition"); - public static void latinIME_commitPartialText(final String committedWord, + private static final LogStatement LOGSTATEMENT_COMMIT_PARTIAL_TEXT = + new LogStatement("CommitPartialText", true, false, "newCursorPosition"); + public static void commitPartialText(final String committedWord, final long lastTimestampOfWordData, final boolean isBatchMode) { final ResearchLogger researchLogger = getInstance(); final String scrubbedWord = scrubDigitsFromString(committedWord); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT); + researchLogger.enqueueEvent(LOGSTATEMENT_COMMIT_PARTIAL_TEXT); researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, lastTimestampOfWordData, isBatchMode); } @@ -1705,12 +1716,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueueEvent(LOGSTATEMENT_ONUSERPAUSE, interval); } - public static void latinIME_handleSeparator() { - // Reset the saved down event time. For tapping, motion events, etc. before the separator - // are assigned to the previous LogUnit, and events after the separator are assigned to the - // next LogUnit. In the case of multitap, this might capture down events corresponding to - // the next word, however it should not be more than a character or two. - getInstance().setSavedDownEventTime(SystemClock.uptimeMillis()); + /** + * Record the current time in case the LogUnit is later split. + * + * If the current logUnitis split, then tapping, motion events, etc. before this time should + * be assigned to one LogUnit, and events after this time should go into the following LogUnit. + */ + public static void recordTimeForLogUnitSplit() { + final ResearchLogger researchLogger = getInstance(); + researchLogger.setSavedDownEventTime(SystemClock.uptimeMillis()); + researchLogger.mSavedDownEventTime = Long.MAX_VALUE; } /** |