aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java29
-rw-r--r--src/main/java/com/anysoftkeyboard/dictionaries/TextEntryState.java62
-rw-r--r--src/test/java/com/anysoftkeyboard/AnySoftKeyboardDictionaryGetWordsTest.java49
-rw-r--r--src/test/java/com/anysoftkeyboard/TestInputConnection.java249
-rw-r--r--src/test/java/com/anysoftkeyboard/TestableAnySoftKeyboard.java189
5 files changed, 351 insertions, 227 deletions
diff --git a/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java b/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java
index 5cce118e7..8b8aa7034 100644
--- a/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java
+++ b/src/main/java/com/anysoftkeyboard/AnySoftKeyboard.java
@@ -66,7 +66,6 @@ import com.anysoftkeyboard.base.dictionaries.EditableDictionary;
import com.anysoftkeyboard.dictionaries.ExternalDictionaryFactory;
import com.anysoftkeyboard.dictionaries.Suggest;
import com.anysoftkeyboard.dictionaries.TextEntryState;
-import com.anysoftkeyboard.dictionaries.TextEntryState.State;
import com.anysoftkeyboard.dictionaries.sqlite.AutoDictionary;
import com.anysoftkeyboard.keyboards.AnyKeyboard;
import com.anysoftkeyboard.keyboards.AnyKeyboard.HardKeyboardTranslator;
@@ -102,9 +101,7 @@ import com.menny.android.anysoftkeyboard.BuildConfig;
import com.menny.android.anysoftkeyboard.R;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Set;
/**
* Input method implementation for QWERTY-ish keyboard.
@@ -646,9 +643,9 @@ public abstract class AnySoftKeyboard extends InputMethodService implements
}
} else {
Log.d(TAG, "onUpdateSelection: not predicting at this moment, maybe the cursor is now at a new word?");
- if (TextEntryState.getState() == State.ACCEPTED_DEFAULT) {
+ if (TextEntryState.willUndoCommitOnBackspace()){
if (mUndoCommitCursorPosition == oldSelStart && mUndoCommitCursorPosition != newSelStart) {
- Log.d(TAG, "onUpdateSelection: I am in ACCEPTED_DEFAULT state, but the user moved the cursor, so it is not possible to undo_commit now.");
+ Log.d(TAG, "onUpdateSelection: I am in a state that is position sensitive but the user moved the cursor, so it is not possible to undo_commit now.");
abortCorrection(true, false);
} else if (mUndoCommitCursorPosition == -2) {
Log.d(TAG, "onUpdateSelection: I am in ACCEPTED_DEFAULT state, time to store the position - I can only undo-commit from here.");
@@ -1973,8 +1970,7 @@ public abstract class AnySoftKeyboard extends InputMethodService implements
if (TextEntryState.getState() == TextEntryState.State.UNDO_COMMIT) {
revertLastWord(deleteChar);
} else if (deleteChar) {
- if (mCandidateView != null
- && mCandidateView.dismissAddToDictionaryHint()) {
+ if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) {
// Go back to the suggestion mode if the user canceled the
// "Touch again to save".
// NOTE: we don't revert the word when backspacing
@@ -2049,7 +2045,6 @@ public abstract class AnySoftKeyboard extends InputMethodService implements
if (BuildConfig.DEBUG) Log.d(TAG, "handleCharacter: %d, isPredictionOn: %s, mPredicting: %s", primaryCode, isPredictionOn(), mPredicting);
mExpectingSelectionUpdateBy = SystemClock.uptimeMillis() + MAX_TIME_TO_EXPECT_SELECTION_UPDATE;
-
if (!mPredicting && isPredictionOn() && isAlphabet(primaryCode) && !isCursorTouchingWord()) {
mPredicting = true;
mUndoCommitCursorPosition = -2;// so it will be marked the next time
@@ -2103,17 +2098,15 @@ public abstract class AnySoftKeyboard extends InputMethodService implements
final int cursorPosition;
if (mWord.cursorPosition() != mWord.length()) {
//Cursor is not at the end of the word. I'll need to reposition
- cursorPosition = getCursorPosition(ic);
+ cursorPosition = mGlobalCursorPosition + 1/*adding the new character*/;
+ ic.beginBatchEdit();
} else {
cursorPosition = -1;
}
- if (cursorPosition >= 0)
- ic.beginBatchEdit();
-
ic.setComposingText(mWord.getTypedWord(), 1);
- if (cursorPosition >= 0) {
- ic.setSelection(cursorPosition + 1, cursorPosition + 1);
+ if (cursorPosition > 0) {
+ ic.setSelection(cursorPosition, cursorPosition);
ic.endBatchEdit();
}
}
@@ -2359,13 +2352,15 @@ public abstract class AnySoftKeyboard extends InputMethodService implements
mJustAutoAddedWord = addToDictionaries(mWord, AutoDictionary.AdditionType.Picked);
}
- final boolean showingAddToDictionaryHint = !mJustAutoAddedWord
+ final boolean showingAddToDictionaryHint =
+ (!mJustAutoAddedWord)
&& index == 0
&& (mQuickFixes || mShowSuggestions)
- && !mSuggest.isValidWord(suggestion)// this is for the case that the word was auto-added upon picking
- && !mSuggest.isValidWord(suggestion.toString().toLowerCase(getCurrentKeyboard().getLocale()));
+ && (!mSuggest.isValidWord(suggestion))// this is for the case that the word was auto-added upon picking
+ && (!mSuggest.isValidWord(suggestion.toString().toLowerCase(getCurrentKeyboard().getLocale())));
if (showingAddToDictionaryHint) {
+ TextEntryState.acceptedSuggestionAddedToDictionary();
if (mCandidateView != null) mCandidateView.showAddToDictionaryHint(suggestion);
} else if (!TextUtils.isEmpty(mCommittedWord) && !mJustAutoAddedWord) {
//showing next-words if:
diff --git a/src/main/java/com/anysoftkeyboard/dictionaries/TextEntryState.java b/src/main/java/com/anysoftkeyboard/dictionaries/TextEntryState.java
index 2422acb74..9ef459daa 100644
--- a/src/main/java/com/anysoftkeyboard/dictionaries/TextEntryState.java
+++ b/src/main/java/com/anysoftkeyboard/dictionaries/TextEntryState.java
@@ -49,6 +49,23 @@ public class TextEntryState {
private static int sActualChars;
+ public static boolean willUndoCommitOnBackspace() {
+ switch (sState) {
+ case ACCEPTED_DEFAULT:
+ case PICKED_TYPED_ADDED_TO_DICTIONARY:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static void acceptedSuggestionAddedToDictionary() {
+ if (BuildConfig.TESTING_BUILD) {
+ if (sState != State.PICKED_SUGGESTION) Log.wtf(TAG, "acceptedSuggestionAddedToDictionary should only be called in a PICKED_SUGGESTION state!");
+ }
+ sState = State.PICKED_TYPED_ADDED_TO_DICTIONARY;
+ }
+
public enum State {
UNKNOWN,
START,
@@ -61,7 +78,8 @@ public class TextEntryState {
SPACE_AFTER_PICKED,
UNDO_COMMIT,
CORRECTING,
- PICKED_CORRECTION;
+ PICKED_CORRECTION,
+ PICKED_TYPED_ADDED_TO_DICTIONARY,
}
private static State sState = State.UNKNOWN;
@@ -129,21 +147,6 @@ public class TextEntryState {
displayState();
}
- // State.ACCEPTED_DEFAULT will be changed to other sub-states
- // (see "case ACCEPTED_DEFAULT" in typedCharacter() below),
- // and should be restored back to State.ACCEPTED_DEFAULT after processing for each sub-state.
- public static void backToAcceptedDefault(CharSequence typedWord) {
- if (typedWord == null) return;
- switch (sState) {
- case SPACE_AFTER_ACCEPTED:
- case PUNCTUATION_AFTER_ACCEPTED:
- case IN_WORD:
- sState = State.ACCEPTED_DEFAULT;
- break;
- }
- displayState();
- }
-
public static void acceptedTyped(CharSequence typedWord) {
sWordNotInDictionaryCount++;
sState = State.PICKED_SUGGESTION;
@@ -164,35 +167,25 @@ public class TextEntryState {
displayState();
}
- public static void selectedForCorrection() {
- sState = State.CORRECTING;
- displayState();
- }
-
public static void typedCharacter(char c, boolean isSeparator) {
boolean isSpace = c == ' ';
switch (sState) {
case IN_WORD:
if (isSpace || isSeparator) {
sState = State.START;
- } else {
- // State hasn't changed.
- }
+ }/* else State hasn't changed.*/
break;
case ACCEPTED_DEFAULT:
case SPACE_AFTER_PICKED:
- if (isSpace) {
- sState = State.SPACE_AFTER_ACCEPTED;
- } else if (isSeparator) {
- sState = State.PUNCTUATION_AFTER_ACCEPTED;
- } else {
- sState = State.IN_WORD;
- }
- break;
case PICKED_SUGGESTION:
case PICKED_CORRECTION:
+ case PICKED_TYPED_ADDED_TO_DICTIONARY:
if (isSpace) {
- sState = State.SPACE_AFTER_PICKED;
+ if (sState == State.ACCEPTED_DEFAULT || sState == State.SPACE_AFTER_PICKED) {
+ sState = State.SPACE_AFTER_ACCEPTED;
+ } else {
+ sState = State.SPACE_AFTER_PICKED;
+ }
} else if (isSeparator) {
// Swap
sState = State.PUNCTUATION_AFTER_ACCEPTED;
@@ -229,7 +222,8 @@ public class TextEntryState {
if (sState == State.ACCEPTED_DEFAULT) {
sState = State.UNDO_COMMIT;
sAutoSuggestUndoneCount++;
- //LatinImeLogger.logOnAutoSuggestionCanceled();
+ } else if (sState == State.PICKED_TYPED_ADDED_TO_DICTIONARY) {
+ sState = State.UNDO_COMMIT;
} else if (sState == State.UNDO_COMMIT) {
sState = State.IN_WORD;
}
diff --git a/src/test/java/com/anysoftkeyboard/AnySoftKeyboardDictionaryGetWordsTest.java b/src/test/java/com/anysoftkeyboard/AnySoftKeyboardDictionaryGetWordsTest.java
index ef20357be..6d761120c 100644
--- a/src/test/java/com/anysoftkeyboard/AnySoftKeyboardDictionaryGetWordsTest.java
+++ b/src/test/java/com/anysoftkeyboard/AnySoftKeyboardDictionaryGetWordsTest.java
@@ -2,6 +2,7 @@ package com.anysoftkeyboard;
import android.view.inputmethod.EditorInfo;
+import com.anysoftkeyboard.api.KeyCodes;
import com.anysoftkeyboard.keyboards.views.CandidateView;
import com.menny.android.anysoftkeyboard.AskGradleTestRunner;
@@ -82,6 +83,8 @@ public class AnySoftKeyboardDictionaryGetWordsTest {
@Test
public void testAskForSuggestionsWithDelayedInputConnectionUpdates() {
+ TestInputConnection inputConnection = (TestInputConnection) mAnySoftKeyboardUnderTest.getCurrentInputConnection();
+ inputConnection.setSendUpdates(false);
verifyNoSuggestionsInteractions(mSpiedCandidateView);
mAnySoftKeyboardUnderTest.simulateKeyPress('h');
verifySuggestions(mSpiedCandidateView, true, "h");
@@ -90,7 +93,7 @@ public class AnySoftKeyboardDictionaryGetWordsTest {
//sending a delayed event from the input-connection.
//this can happen when the user is clicking fast (in ASK thread), but the other side (the app thread)
//is too slow, or busy with something to send out events.
- mAnySoftKeyboardUnderTest.updateInputConnection('h');
+ inputConnection.sendUpdateNow();
mAnySoftKeyboardUnderTest.simulateKeyPress('l');
verifySuggestions(mSpiedCandidateView, true, "hel", "hell", "hello");
@@ -111,7 +114,7 @@ public class AnySoftKeyboardDictionaryGetWordsTest {
@Test
public void testAutoPickWordWhenCursorAtTheEndOfTheWord() {
- TestableAnySoftKeyboard.TestInputConnection inputConnection = (TestableAnySoftKeyboard.TestInputConnection) mAnySoftKeyboardUnderTest.getCurrentInputConnection();
+ TestInputConnection inputConnection = (TestInputConnection) mAnySoftKeyboardUnderTest.getCurrentInputConnection();
verifyNoSuggestionsInteractions(mSpiedCandidateView);
mAnySoftKeyboardUnderTest.simulateTextTyping("h");
verifySuggestions(mSpiedCandidateView, true, "h");
@@ -120,14 +123,16 @@ public class AnySoftKeyboardDictionaryGetWordsTest {
mAnySoftKeyboardUnderTest.simulateTextTyping("l");
verifySuggestions(mSpiedCandidateView, true, "hel", "hell", "hello");
- Assert.assertEquals("", inputConnection.getLastCommitText());
+ Assert.assertEquals("", inputConnection.getLastCommitCorrection());
mAnySoftKeyboardUnderTest.simulateKeyPress(' ');
- Assert.assertEquals("hell", inputConnection.getLastCommitText());
+ Assert.assertEquals("hell", inputConnection.getLastCommitCorrection());
+ //we should also see the space
+ Assert.assertEquals("hell ", inputConnection.getCurrentTextInInputConnection());
}
@Test
public void testDoesNotAutoPickWordWhenCursorNotAtTheEndOfTheWord() {
- TestableAnySoftKeyboard.TestInputConnection inputConnection = (TestableAnySoftKeyboard.TestInputConnection) mAnySoftKeyboardUnderTest.getCurrentInputConnection();
+ TestInputConnection inputConnection = (TestInputConnection) mAnySoftKeyboardUnderTest.getCurrentInputConnection();
verifyNoSuggestionsInteractions(mSpiedCandidateView);
mAnySoftKeyboardUnderTest.simulateTextTyping("h");
verifySuggestions(mSpiedCandidateView, true, "h");
@@ -140,24 +145,48 @@ public class AnySoftKeyboardDictionaryGetWordsTest {
verifySuggestions(mSpiedCandidateView, true, "hel", "hell", "hello");
Mockito.reset(inputConnection);//clearing any previous interactions with finishComposingText
- Assert.assertEquals("", inputConnection.getLastCommitText());
+ Assert.assertEquals("", inputConnection.getLastCommitCorrection());
mAnySoftKeyboardUnderTest.simulateKeyPress(' ');
//this time, it will not auto-pick since the cursor is inside the word (and not at the end)
- Assert.assertEquals("", inputConnection.getLastCommitText());
+ Assert.assertEquals("", inputConnection.getLastCommitCorrection());
//will stop composing in the input-connection
Mockito.verify(inputConnection).finishComposingText();
//also, it will abort suggestions
verifySuggestions(mSpiedCandidateView, true);
}
+ @Test
+ public void testBackSpaceCorrectlyWhenEditingManuallyPickedWord() {
+ //related to https://github.com/AnySoftKeyboard/AnySoftKeyboard/issues/585
+ TestInputConnection inputConnection = (TestInputConnection) mAnySoftKeyboardUnderTest.getCurrentInputConnection();
+
+ verifyNoSuggestionsInteractions(mSpiedCandidateView);
+ mAnySoftKeyboardUnderTest.simulateTextTyping("hel");
+ verifySuggestions(mSpiedCandidateView, true, "hel", "hell", "hello");
+
+ Assert.assertEquals("", inputConnection.getLastCommitCorrection());
+ mAnySoftKeyboardUnderTest.pickSuggestionManually(0, "hel");
+ //at this point, the candidates view will show a hint
+ Mockito.verify(mAnySoftKeyboardUnderTest.getMockCandidateView()).showAddToDictionaryHint("hel");
+ Assert.assertEquals("hel ", inputConnection.getCurrentTextInInputConnection());
+ //now, navigating to to the 'e'
+ inputConnection.setSelection(2, 2);
+ Assert.assertEquals("hel ", inputConnection.getCurrentTextInInputConnection());
+ Assert.assertEquals(2, inputConnection.getCurrentStartPosition());
+ mAnySoftKeyboardUnderTest.simulateKeyPress(KeyCodes.DELETE, true);
+ Assert.assertEquals("hl ", inputConnection.getCurrentTextInInputConnection());
+ Assert.assertEquals(1, inputConnection.getCurrentStartPosition());
+ }
+
private void verifyNoSuggestionsInteractions(CandidateView candidateView) {
Mockito.verify(candidateView, Mockito.never()).setSuggestions(Mockito.anyList(), Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyBoolean());
}
private void verifySuggestions(CandidateView candidateView, boolean resetCandidateView, CharSequence... expectedSuggestions) {
ArgumentCaptor<List> suggestionsCaptor = ArgumentCaptor.forClass(List.class);
- Mockito.verify(candidateView).setSuggestions(suggestionsCaptor.capture(), Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyBoolean());
- List actualSuggestions = suggestionsCaptor.getValue();
+ Mockito.verify(candidateView, Mockito.atLeastOnce()).setSuggestions(suggestionsCaptor.capture(), Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyBoolean());
+ List<List> allValues = suggestionsCaptor.getAllValues();
+ List actualSuggestions = allValues.get(allValues.size()-1);
if (expectedSuggestions.length == 0) {
Assert.assertTrue(actualSuggestions == null || actualSuggestions.size() == 0);
} else {
@@ -168,6 +197,6 @@ public class AnySoftKeyboardDictionaryGetWordsTest {
}
}
- if (resetCandidateView) Mockito.reset(candidateView);
+ if (resetCandidateView) mAnySoftKeyboardUnderTest.resetMockCandidateView();
}
} \ No newline at end of file
diff --git a/src/test/java/com/anysoftkeyboard/TestInputConnection.java b/src/test/java/com/anysoftkeyboard/TestInputConnection.java
new file mode 100644
index 000000000..15e913ce1
--- /dev/null
+++ b/src/test/java/com/anysoftkeyboard/TestInputConnection.java
@@ -0,0 +1,249 @@
+package com.anysoftkeyboard;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.UnderlineSpan;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+public class TestInputConnection implements InputConnection {
+
+ @NonNull
+ private UnderlineSpan mCurrentComposingSpan = new UnderlineSpan();
+ private boolean mSendUpdates = true;
+ private boolean mInEditMode = false;
+ private boolean mChangesWhileInEdit = false;
+
+ private int mCursorPosition = 0;
+ private SpannableStringBuilder mInputText = new SpannableStringBuilder();
+ @NonNull
+ private final AnySoftKeyboard mIme;
+
+ private String mLastCommitCorrection = "";
+
+ public TestInputConnection(@NonNull AnySoftKeyboard ime) {
+ mIme = ime;
+ }
+
+ @Override
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ String unspanned = mInputText.toString();
+ int start = Math.max(0, mCursorPosition - n);
+ int end = Math.min(mInputText.length(), mCursorPosition);
+ return unspanned.substring(start, end);
+ }
+
+ @Override
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ String unspanned = mInputText.toString();
+ int start = Math.max(0, mCursorPosition);
+ int end = Math.min(mInputText.length(), mCursorPosition + n);
+ return unspanned.substring(start, end);
+ }
+
+ @Override
+ public CharSequence getSelectedText(int flags) {
+ return "";
+ }
+
+ @Override
+ public int getCursorCapsMode(int reqModes) {
+ return 0;
+ }
+
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ ExtractedText extracted = new ExtractedText();
+ extracted.startOffset = 0;
+ extracted.selectionStart = mCursorPosition;
+ extracted.selectionEnd = mCursorPosition;
+
+ return extracted;
+ }
+
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ if (beforeLength == 0 && afterLength == 0) return true;
+
+ final int deleteStart = Math.max(mCursorPosition - beforeLength, 0);
+ final int deleteEnd = Math.min(mCursorPosition + afterLength, mInputText.length());
+ mInputText.delete(deleteStart, deleteEnd);
+ final int cursorDelta = mCursorPosition - deleteStart;
+ notifyTextChange(-cursorDelta);
+ return true;
+ }
+
+ private void notifyTextChange(int cursorDelta) {
+ final int oldPosition = mCursorPosition;
+ mCursorPosition += cursorDelta;
+ if (mInEditMode) {
+ mChangesWhileInEdit = true;
+ } else {
+ int[] composedTextRange = findComposedText();
+ if (mSendUpdates) mIme.onUpdateSelection(oldPosition, oldPosition, mCursorPosition, mCursorPosition, composedTextRange[0], composedTextRange[1]);
+ }
+ }
+
+ public void setSendUpdates(boolean sendUpdates) {
+ mSendUpdates = sendUpdates;
+ }
+
+ public void sendUpdateNow() {
+ final boolean originalSendState = mSendUpdates;
+ mSendUpdates = true;
+ notifyTextChange(0);
+ mSendUpdates = originalSendState;
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ commitTextAs(text, true);
+ return true;
+ }
+
+ private void commitTextAs(CharSequence text, boolean asComposing) {
+ int[] composedTextRange = findComposedText();
+ mInputText.delete(composedTextRange[0], composedTextRange[1]);
+ final int textRemoved = (composedTextRange[1] - composedTextRange[0]);
+ mInputText.append(asComposing? asComposeText(text) : text);
+ notifyTextChange(text.length() - textRemoved);
+ }
+
+ private int[] findComposedText() {
+ int start = mInputText.getSpanStart(mCurrentComposingSpan);
+ int end = mInputText.getSpanEnd(mCurrentComposingSpan);
+ if (start == -1) return new int[] {mCursorPosition, mCursorPosition};
+ else return new int[] {start, end};
+ }
+
+ private CharSequence asComposeText(CharSequence text) {
+ mCurrentComposingSpan = new UnderlineSpan();
+ SpannableString composed = new SpannableString(text);
+ composed.setSpan(mCurrentComposingSpan, 0, text.length(), 0);
+ return composed;
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end) {
+ return false;
+ }
+
+ @Override
+ public boolean finishComposingText() {
+ mInputText.clearSpans();
+ return true;
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ commitTextAs(text, false);
+ return true;
+ }
+
+ @Override
+ public boolean commitCompletion(CompletionInfo text) {
+ return false;
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ @Override
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ mLastCommitCorrection = correctionInfo.getNewText().toString();
+ return true;
+ }
+
+ public String getLastCommitCorrection() {
+ return mLastCommitCorrection;
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ if (start == end && start == mCursorPosition) return true;
+
+ notifyTextChange(start - mCursorPosition);
+
+ return true;
+ }
+
+ @Override
+ public boolean performEditorAction(int editorAction) {
+ return false;
+ }
+
+ @Override
+ public boolean performContextMenuAction(int id) {
+ return false;
+ }
+
+ @Override
+ public boolean beginBatchEdit() {
+ mInEditMode = true;
+ return true;
+ }
+
+ @Override
+ public boolean endBatchEdit() {
+ mInEditMode = false;
+ if (mChangesWhileInEdit) sendUpdateNow();
+ mChangesWhileInEdit = false;
+ return true;
+ }
+
+ @Override
+ public boolean sendKeyEvent(KeyEvent event) {
+ /*
+ ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
+ ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
+ KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
+ */
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ //only handling UP events
+ if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
+ deleteSurroundingText(1, 0);
+ } else if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE) {
+ commitText(" ", 1);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean clearMetaKeyStates(int states) {
+ return true;
+ }
+
+ @Override
+ public boolean reportFullscreenMode(boolean enabled) {
+ return false;
+ }
+
+ @Override
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return false;
+ }
+
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode) {
+ return false;
+ }
+
+ @NonNull
+ public String getCurrentTextInInputConnection() {
+ return mInputText.toString();
+ }
+
+ public int getCurrentStartPosition() {
+ return mCursorPosition;
+ }
+}
diff --git a/src/test/java/com/anysoftkeyboard/TestableAnySoftKeyboard.java b/src/test/java/com/anysoftkeyboard/TestableAnySoftKeyboard.java
index 852f7e9a4..0abdc7e63 100644
--- a/src/test/java/com/anysoftkeyboard/TestableAnySoftKeyboard.java
+++ b/src/test/java/com/anysoftkeyboard/TestableAnySoftKeyboard.java
@@ -1,17 +1,9 @@
package com.anysoftkeyboard;
-import android.annotation.TargetApi;
import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
import android.support.annotation.NonNull;
-import android.view.KeyEvent;
import android.view.View;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import com.anysoftkeyboard.addons.AddOn;
@@ -31,6 +23,8 @@ import com.menny.android.anysoftkeyboard.SoftKeyboard;
import org.junit.Assert;
import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.robolectric.Robolectric;
import org.robolectric.shadows.ShadowSystemClock;
@@ -50,6 +44,7 @@ public class TestableAnySoftKeyboard extends SoftKeyboard {
private CandidateView mMockCandidateView;
private UserDictionary mSpiedUserDictionary;
private boolean mHidden = true;
+ private boolean mCandidateShowsHint = false;
public Suggest getSpiedSuggest() {
return mSpiedSuggest;
@@ -81,10 +76,30 @@ public class TestableAnySoftKeyboard extends SoftKeyboard {
public View onCreateCandidatesView() {
View spiedRootView = Mockito.spy(super.onCreateCandidatesView());
mMockCandidateView = Mockito.mock(CandidateView.class);
+ resetMockCandidateView();
Mockito.doReturn(mMockCandidateView).when(spiedRootView).findViewById(R.id.candidates);
return spiedRootView;
}
+ public void resetMockCandidateView() {
+ Mockito.reset(mMockCandidateView);
+ Mockito.doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ boolean previousState = mCandidateShowsHint;
+ mCandidateShowsHint = false;
+ return previousState;
+ }
+ }).when(mMockCandidateView).dismissAddToDictionaryHint();
+ Mockito.doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ mCandidateShowsHint = true;
+ return null;
+ }
+ }).when(mMockCandidateView).showAddToDictionaryHint(Mockito.any(CharSequence.class));
+ }
+
@Override
public EditorInfo getCurrentInputEditorInfo() {
return mEditorInfo;
@@ -147,7 +162,6 @@ public class TestableAnySoftKeyboard extends SoftKeyboard {
if (asDiscreteKeys) {
for (char key : text.toCharArray()) {
simulateKeyPress(key, advanceTime);
- updateInputConnection(key);
}
} else {
onText(null, text);
@@ -156,10 +170,6 @@ public class TestableAnySoftKeyboard extends SoftKeyboard {
}
}
- public void updateInputConnection(char key) {
- mInputConnection.appendToInput(key);
- }
-
public void simulateKeyPress(final int keyCode, final boolean advanceTime) {
onPress(keyCode);
Robolectric.flushForegroundThreadScheduler();
@@ -253,157 +263,4 @@ public class TestableAnySoftKeyboard extends SoftKeyboard {
}
}
- public static class TestInputConnection implements InputConnection {
-
- private int mCursorPosition = 0;
- private StringBuilder mInputText = new StringBuilder();
- @NonNull
- private final AnySoftKeyboard mIme;
-
- private String mLastCommitText = "";
-
- public TestInputConnection(@NonNull AnySoftKeyboard ime) {
- mIme = ime;
- }
-
- @Override
- public CharSequence getTextBeforeCursor(int n, int flags) {
- return mInputText.substring(Math.max(0, mCursorPosition - n), Math.min(mInputText.length(), mCursorPosition));
- }
-
- @Override
- public CharSequence getTextAfterCursor(int n, int flags) {
- return mInputText.substring(Math.max(0, mCursorPosition), Math.min(mInputText.length(), mCursorPosition + n));
- }
-
- @Override
- public CharSequence getSelectedText(int flags) {
- return "";
- }
-
- @Override
- public int getCursorCapsMode(int reqModes) {
- return 0;
- }
-
- @Override
- public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
- ExtractedText extracted = new ExtractedText();
- extracted.startOffset = 0;
- extracted.selectionStart = mCursorPosition;
- extracted.selectionEnd = mCursorPosition;
-
- return extracted;
- }
-
- @Override
- public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- //String beforeText = mInputText.substring(0, Math.min(mInputText.length(), mCursorPosition-beforeLength));
- //String afterText = mInputText.substring(mCursorPosition+afterLength);
- mInputText.delete(mCursorPosition-beforeLength, mCursorPosition+afterLength);// = beforeText+afterText;
- notifyTextChange(-beforeLength);
- return true;
- }
-
- private void notifyTextChange(int cursorDelta) {
- final int oldPosition = mCursorPosition;
- mCursorPosition += cursorDelta;
- mIme.onUpdateSelection(oldPosition, oldPosition, mCursorPosition, mCursorPosition, 0, mInputText.length());
- }
-
- @Override
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return false;
- }
-
- @Override
- public boolean setComposingRegion(int start, int end) {
- return false;
- }
-
- @Override
- public boolean finishComposingText() {
- return false;
- }
-
- @Override
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return true;
- }
-
- @Override
- public boolean commitCompletion(CompletionInfo text) {
- return false;
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- @Override
- public boolean commitCorrection(CorrectionInfo correctionInfo) {
- mLastCommitText = correctionInfo.getNewText().toString();
- return true;
- }
-
- public String getLastCommitText() {
- return mLastCommitText;
- }
-
- @Override
- public boolean setSelection(int start, int end) {
- if (start != mCursorPosition) {
- notifyTextChange(start-mCursorPosition);
- }
-
- return true;
- }
-
- @Override
- public boolean performEditorAction(int editorAction) {
- return false;
- }
-
- @Override
- public boolean performContextMenuAction(int id) {
- return false;
- }
-
- @Override
- public boolean beginBatchEdit() {
- return true;
- }
-
- @Override
- public boolean endBatchEdit() {
- return true;
- }
-
- @Override
- public boolean sendKeyEvent(KeyEvent event) {
- return true;
- }
-
- @Override
- public boolean clearMetaKeyStates(int states) {
- return true;
- }
-
- @Override
- public boolean reportFullscreenMode(boolean enabled) {
- return false;
- }
-
- @Override
- public boolean performPrivateCommand(String action, Bundle data) {
- return false;
- }
-
- @Override
- public boolean requestCursorUpdates(int cursorUpdateMode) {
- return false;
- }
-
- public void appendToInput(char key) {
- mInputText.insert(mCursorPosition, key);
- notifyTextChange(1);
- }
- }
}