[IL121] Introduce InputTransaction

We probably can't put this off any longer

Bug: 8636060
Change-Id: I1e5d3cf62d719f4d064ced3282bebf2e822f6baa
This commit is contained in:
Jean Chalard 2014-03-04 15:29:54 +09:00
parent 25d912df58
commit 7b905c40e9
2 changed files with 97 additions and 29 deletions

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.event;
/**
* An object encapsulating a single transaction for input.
*/
public class InputTransaction {
// UPDATE_LATER is stronger than UPDATE_NOW. The reason for this is, if we have to update later,
// it's because something will change that we can't evaluate now, which means that even if we
// re-evaluate now we'll have to do it again later. The only case where that wouldn't apply
// would be if we needed to update now to find out the new state right away, but then we
// can't do it with this deferred mechanism anyway.
public static final int SHIFT_NO_UPDATE = 0;
public static final int SHIFT_UPDATE_NOW = 1;
public static final int SHIFT_UPDATE_LATER = 2;
// Initial conditions
public final int mShiftState;
// Outputs
private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
public InputTransaction(final int shiftState) {
mShiftState = shiftState;
}
public void requireShiftUpdate(final int updateType) {
mRequiredShiftUpdate = Math.max(mRequiredShiftUpdate, updateType);
}
public int getRequiredShiftUpdate() {
return mRequiredShiftUpdate;
}
}

View file

@ -23,12 +23,12 @@ import android.text.style.SuggestionSpan;
import android.util.Log; import android.util.Log;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.event.EventInterpreter; import com.android.inputmethod.event.EventInterpreter;
import com.android.inputmethod.event.InputTransaction;
import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Dictionary;
@ -365,6 +365,8 @@ public final class InputLogic {
ResearchLogger.latinIME_onCodeInput(code, x, y); ResearchLogger.latinIME_onCodeInput(code, x, y);
} }
final long when = SystemClock.uptimeMillis(); final long when = SystemClock.uptimeMillis();
final InputTransaction inputTransaction = new InputTransaction(
getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
if (code != Constants.CODE_DELETE if (code != Constants.CODE_DELETE
|| when > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) { || when > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
mDeleteCount = 0; mDeleteCount = 0;
@ -389,12 +391,12 @@ public final class InputLogic {
boolean didAutoCorrect = false; boolean didAutoCorrect = false;
switch (code) { switch (code) {
case Constants.CODE_DELETE: case Constants.CODE_DELETE:
handleBackspace(settingsValues, spaceState, handler, keyboardSwitcher); handleBackspace(settingsValues, spaceState, inputTransaction, handler);
LatinImeLogger.logOnDelete(x, y); LatinImeLogger.logOnDelete(x, y);
break; break;
case Constants.CODE_SHIFT: case Constants.CODE_SHIFT:
performRecapitalization(settingsValues); performRecapitalization(settingsValues);
keyboardSwitcher.updateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
break; break;
case Constants.CODE_CAPSLOCK: case Constants.CODE_CAPSLOCK:
// Note: Changing keyboard to shift lock state is handled in // Note: Changing keyboard to shift lock state is handled in
@ -449,12 +451,12 @@ public final class InputLogic {
// No action label, and the action from imeOptions is NONE: this is a regular // No action label, and the action from imeOptions is NONE: this is a regular
// enter key that should input a carriage return. // enter key that should input a carriage return.
didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER, didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER,
x, y, spaceState, keyboardSwitcher, handler); x, y, spaceState, inputTransaction, handler);
} }
break; break;
case Constants.CODE_SHIFT_ENTER: case Constants.CODE_SHIFT_ENTER:
didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER, didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER,
x, y, spaceState, keyboardSwitcher, handler); x, y, spaceState, inputTransaction, handler);
break; break;
case Constants.CODE_ALPHA_FROM_EMOJI: case Constants.CODE_ALPHA_FROM_EMOJI:
// Note: Switching back from Emoji keyboard to the main keyboard is being handled in // Note: Switching back from Emoji keyboard to the main keyboard is being handled in
@ -462,7 +464,7 @@ public final class InputLogic {
break; break;
default: default:
didAutoCorrect = handleNonSpecialCharacter(settingsValues, didAutoCorrect = handleNonSpecialCharacter(settingsValues,
code, x, y, spaceState, keyboardSwitcher, handler); code, x, y, spaceState, inputTransaction, handler);
break; break;
} }
// Reset after any single keystroke, except shift, capslock, and symbol-shift // Reset after any single keystroke, except shift, capslock, and symbol-shift
@ -474,6 +476,15 @@ public final class InputLogic {
mEnteredText = null; mEnteredText = null;
} }
mConnection.endBatchEdit(); mConnection.endBatchEdit();
switch (inputTransaction.getRequiredShiftUpdate()) {
case InputTransaction.SHIFT_UPDATE_LATER:
mLatinIME.mHandler.postUpdateShiftState();
break;
case InputTransaction.SHIFT_UPDATE_NOW:
keyboardSwitcher.updateShiftState();
break;
default: // SHIFT_NO_UPDATE
}
} }
public void onStartBatchInput(final SettingsValues settingsValues, public void onStartBatchInput(final SettingsValues settingsValues,
@ -622,18 +633,20 @@ public final class InputLogic {
* @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. * @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
* @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. * @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
* @param spaceState the space state at start of the batch input. * @param spaceState the space state at start of the batch input.
* @param inputTransaction The transaction in progress.
* @return whether this caused an auto-correction to happen. * @return whether this caused an auto-correction to happen.
*/ */
private boolean handleNonSpecialCharacter(final SettingsValues settingsValues, private boolean handleNonSpecialCharacter(final SettingsValues settingsValues,
final int codePoint, final int x, final int y, final int spaceState, final int codePoint, final int x, final int y, final int spaceState,
// TODO: remove these arguments final InputTransaction inputTransaction,
final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { // TODO: remove this argument
final LatinIME.UIHandler handler) {
mSpaceState = SpaceState.NONE; mSpaceState = SpaceState.NONE;
final boolean didAutoCorrect; final boolean didAutoCorrect;
if (settingsValues.isWordSeparator(codePoint) if (settingsValues.isWordSeparator(codePoint)
|| Character.getType(codePoint) == Character.OTHER_SYMBOL) { || Character.getType(codePoint) == Character.OTHER_SYMBOL) {
didAutoCorrect = handleSeparator(settingsValues, codePoint, didAutoCorrect = handleSeparator(settingsValues, codePoint,
Constants.SUGGESTION_STRIP_COORDINATE == x, spaceState, keyboardSwitcher, Constants.SUGGESTION_STRIP_COORDINATE == x, spaceState, inputTransaction,
handler); handler);
if (settingsValues.mIsInternal) { if (settingsValues.mIsInternal) {
LatinImeLoggerUtils.onSeparator((char)codePoint, x, y); LatinImeLoggerUtils.onSeparator((char)codePoint, x, y);
@ -657,7 +670,7 @@ public final class InputLogic {
} }
} }
handleNonSeparator(settingsValues, codePoint, x, y, spaceState, handleNonSeparator(settingsValues, codePoint, x, y, spaceState,
keyboardSwitcher, handler); inputTransaction, handler);
} }
return didAutoCorrect; return didAutoCorrect;
} }
@ -669,11 +682,13 @@ public final class InputLogic {
* @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. * @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
* @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. * @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
* @param spaceState the space state at start of the batch input. * @param spaceState the space state at start of the batch input.
* @param inputTransaction The transaction in progress.
*/ */
private void handleNonSeparator(final SettingsValues settingsValues, private void handleNonSeparator(final SettingsValues settingsValues,
final int codePoint, final int x, final int y, final int spaceState, final int codePoint, final int x, final int y, final int spaceState,
// TODO: Remove these arguments final InputTransaction inputTransaction,
final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { // TODO: Remove this argument
final LatinIME.UIHandler handler) {
// TODO: refactor this method to stop flipping isComposingWord around all the time, and // TODO: refactor this method to stop flipping isComposingWord around all the time, and
// make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
// which has the same name as other handle* methods but is not the same. // which has the same name as other handle* methods but is not the same.
@ -729,8 +744,7 @@ public final class InputLogic {
// We pass 1 to getPreviousWordForSuggestion because we were not composing a word // We pass 1 to getPreviousWordForSuggestion because we were not composing a word
// yet, so the word we want is the 1st word before the cursor. // yet, so the word we want is the 1st word before the cursor.
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), inputTransaction.mShiftState, getNthPreviousWordForSuggestion(
getNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
} }
mConnection.setComposingText(getTextWithUnderline( mConnection.setComposingText(getTextWithUnderline(
@ -742,7 +756,7 @@ public final class InputLogic {
sendKeyCodePoint(settingsValues, codePoint); sendKeyCodePoint(settingsValues, codePoint);
if (swapWeakSpace) { if (swapWeakSpace) {
swapSwapperAndSpace(keyboardSwitcher); swapSwapperAndSpace(inputTransaction);
mSpaceState = SpaceState.WEAK; mSpaceState = SpaceState.WEAK;
} }
// In case the "add to dictionary" hint was still displayed. // In case the "add to dictionary" hint was still displayed.
@ -760,12 +774,14 @@ public final class InputLogic {
* @param codePoint the code point associated with the key. * @param codePoint the code point associated with the key.
* @param isFromSuggestionStrip whether this code point comes from the suggestion strip. * @param isFromSuggestionStrip whether this code point comes from the suggestion strip.
* @param spaceState the space state at start of the batch input. * @param spaceState the space state at start of the batch input.
* @param inputTransaction The transaction in progress.
* @return whether this caused an auto-correction to happen. * @return whether this caused an auto-correction to happen.
*/ */
private boolean handleSeparator(final SettingsValues settingsValues, private boolean handleSeparator(final SettingsValues settingsValues,
final int codePoint, final boolean isFromSuggestionStrip, final int spaceState, final int codePoint, final boolean isFromSuggestionStrip, final int spaceState,
// TODO: remove these arguments final InputTransaction inputTransaction,
final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { // TODO: remove this argument
final LatinIME.UIHandler handler) {
boolean didAutoCorrect = false; boolean didAutoCorrect = false;
// We avoid sending spaces in languages without spaces if we were composing. // We avoid sending spaces in languages without spaces if we were composing.
final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint
@ -820,7 +836,7 @@ public final class InputLogic {
if (Constants.CODE_SPACE == codePoint) { if (Constants.CODE_SPACE == codePoint) {
if (settingsValues.isSuggestionsRequested()) { if (settingsValues.isSuggestionsRequested()) {
if (maybeDoubleSpacePeriod(settingsValues, handler)) { if (maybeDoubleSpacePeriod(settingsValues, handler)) {
keyboardSwitcher.updateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
mSpaceState = SpaceState.DOUBLE; mSpaceState = SpaceState.DOUBLE;
} else if (!mSuggestedWords.isPunctuationSuggestions()) { } else if (!mSuggestedWords.isPunctuationSuggestions()) {
mSpaceState = SpaceState.WEAK; mSpaceState = SpaceState.WEAK;
@ -831,7 +847,7 @@ public final class InputLogic {
handler.postUpdateSuggestionStrip(); handler.postUpdateSuggestionStrip();
} else { } else {
if (swapWeakSpace) { if (swapWeakSpace) {
swapSwapperAndSpace(keyboardSwitcher); swapSwapperAndSpace(inputTransaction);
mSpaceState = SpaceState.SWAP_PUNCTUATION; mSpaceState = SpaceState.SWAP_PUNCTUATION;
} else if ((SpaceState.PHANTOM == spaceState } else if ((SpaceState.PHANTOM == spaceState
&& settingsValues.isUsuallyFollowedBySpace(codePoint)) && settingsValues.isUsuallyFollowedBySpace(codePoint))
@ -856,7 +872,7 @@ public final class InputLogic {
mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
} }
keyboardSwitcher.updateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
return didAutoCorrect; return didAutoCorrect;
} }
@ -864,17 +880,19 @@ public final class InputLogic {
* Handle a press on the backspace key. * Handle a press on the backspace key.
* @param settingsValues The current settings values. * @param settingsValues The current settings values.
* @param spaceState The space state at start of this batch edit. * @param spaceState The space state at start of this batch edit.
* @param inputTransaction The transaction in progress.
*/ */
private void handleBackspace(final SettingsValues settingsValues, final int spaceState, private void handleBackspace(final SettingsValues settingsValues, final int spaceState,
// TODO: remove these arguments final InputTransaction inputTransaction,
final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) { // TODO: remove this argument
final LatinIME.UIHandler handler) {
mSpaceState = SpaceState.NONE; mSpaceState = SpaceState.NONE;
mDeleteCount++; mDeleteCount++;
// In many cases, we may have to put the keyboard in auto-shift state again. However // In many cases, we may have to put the keyboard in auto-shift state again. However
// we want to wait a few milliseconds before doing it to avoid the keyboard flashing // we want to wait a few milliseconds before doing it to avoid the keyboard flashing
// during key repeat. // during key repeat.
handler.postUpdateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_LATER);
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection // If we are in the middle of a recorrection, we need to commit the recorrection
@ -900,7 +918,7 @@ public final class InputLogic {
if (!mWordComposer.isComposingWord()) { if (!mWordComposer.isComposingWord()) {
// If we just removed the last character, auto-caps mode may have changed so we // If we just removed the last character, auto-caps mode may have changed so we
// need to re-evaluate. // need to re-evaluate.
keyboardSwitcher.updateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
} }
} else { } else {
if (mLastComposedWord.canRevertCommit()) { if (mLastComposedWord.canRevertCommit()) {
@ -1012,7 +1030,7 @@ public final class InputLogic {
true /* includeResumedWordInSuggestions */); true /* includeResumedWordInSuggestions */);
} }
// We just removed at least one character. We need to update the auto-caps state. // We just removed at least one character. We need to update the auto-caps state.
keyboardSwitcher.updateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
} }
} }
@ -1028,9 +1046,9 @@ public final class InputLogic {
* *
* This method will check that there are two characters before the cursor and that the first * This method will check that there are two characters before the cursor and that the first
* one is a space before it does the actual swapping. * one is a space before it does the actual swapping.
* @param inputTransaction The transaction in progress.
*/ */
// TODO: Remove this argument private void swapSwapperAndSpace(final InputTransaction inputTransaction) {
private void swapSwapperAndSpace(final KeyboardSwitcher keyboardSwitcher) {
final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
// It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) { if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) {
@ -1040,7 +1058,7 @@ public final class InputLogic {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_swapSwapperAndSpace(lastTwo, text); ResearchLogger.latinIME_swapSwapperAndSpace(lastTwo, text);
} }
keyboardSwitcher.updateShiftState(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
} }
} }
@ -1476,7 +1494,9 @@ public final class InputLogic {
*/ */
private int getActualCapsMode(final SettingsValues settingsValues, private int getActualCapsMode(final SettingsValues settingsValues,
final int keyboardShiftMode) { final int keyboardShiftMode) {
if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) return keyboardShiftMode; if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) {
return keyboardShiftMode;
}
final int auto = getCurrentAutoCapsState(settingsValues); final int auto = getCurrentAutoCapsState(settingsValues);
if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) { if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED; return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;